diff --git a/app/controllers/ui/devices_controller.rb b/app/controllers/ui/devices_controller.rb index cbc9e3a8e..1a99d83b8 100644 --- a/app/controllers/ui/devices_controller.rb +++ b/app/controllers/ui/devices_controller.rb @@ -164,6 +164,7 @@ def device_params :notify_stopped_publishing, :hardware_version_override, :mac_address, + :forwarding_destination_id, { :tag_ids => [] }, { :postprocessing_attributes => :hardware_url }, ) diff --git a/app/controllers/v0/devices_controller.rb b/app/controllers/v0/devices_controller.rb index 3a771be54..331800651 100644 --- a/app/controllers/v0/devices_controller.rb +++ b/app/controllers/v0/devices_controller.rb @@ -106,8 +106,19 @@ def device_params # Researchers + Admins can update is_test and enable_forwarding if current_user.is_admin_or_researcher? - params_to_permit.push({postprocessing_attributes: [:blueprint_url, :hardware_url, :latest_postprocessing, :meta, :forwarding_params]}) + params_to_permit.push( + { + postprocessing_attributes: [ + :blueprint_url, + :hardware_url, + :latest_postprocessing, + :meta, + :forwarding_params + ] + } + ) params_to_permit.push(:enable_forwarding) + params_to_permit.push(:forwarding_destination_id) end if current_user.is_admin? diff --git a/app/lib/presenters/device_presenter.rb b/app/lib/presenters/device_presenter.rb index 27565b53c..b2550c932 100644 --- a/app/lib/presenters/device_presenter.rb +++ b/app/lib/presenters/device_presenter.rb @@ -43,8 +43,9 @@ def data_policy { is_private: device.is_private, enable_forwarding: device.enable_forwarding, - precise_location: device.precise_location - } + precise_location: device.precise_location, + forwarding_destination: device.forwarding_destination&.name + }.compact end end diff --git a/app/models/device.rb b/app/models/device.rb index efa9f83be..9071635f2 100644 --- a/app/models/device.rb +++ b/app/models/device.rb @@ -22,6 +22,7 @@ class Device < ActiveRecord::Base multisearchable :against => [:name, :description, :city, :country_name], if: :active? belongs_to :owner, class_name: 'User' + belongs_to :forwarding_destination, optional: true has_many :devices_tags, dependent: :destroy has_many :tags, through: :devices_tags @@ -117,7 +118,7 @@ def self.ransackable_associations(auth_object = nil) [ "components", "devices_tags", "owner", "pg_search_document" , "postprocessing", "sensors", - "tags" + "tags", "forwarding_destination" ] end @@ -296,7 +297,8 @@ def data_policy(authorized=false) { is_private: is_private, enable_forwarding: authorized ? enable_forwarding : "[FILTERED]", - precise_location: authorized ? precise_location : "[FILTERED]" + precise_location: authorized ? precise_location : "[FILTERED]", + forwarding_destination: authorized ? forwarding_destination&.name : "[FILTERED]" } end diff --git a/app/models/forwarding_destination.rb b/app/models/forwarding_destination.rb new file mode 100644 index 000000000..f677fd1cd --- /dev/null +++ b/app/models/forwarding_destination.rb @@ -0,0 +1,8 @@ +class ForwardingDestination < ActiveRecord::Base + has_many :devices + validates_uniqueness_of :name + + def self.ransackable_attributes(_auth_object = nil) + ["name"] + end +end diff --git a/app/views/ui/devices/_fields.html.erb b/app/views/ui/devices/_fields.html.erb index d7a338308..a844073d1 100644 --- a/app/views/ui/devices/_fields.html.erb +++ b/app/views/ui/devices/_fields.html.erb @@ -48,6 +48,10 @@

<%= t(:device_form_researcher_options_subhead) %>

<% if device.owner.forward_device_readings? %> <%= form.check_box :enable_forwarding, label: t(:device_form_enable_forwarding_label) %> + <%= form.select :forwarding_destination_id, options_for_select( + ForwardingDestination.all.map { |fd| [fd.name, fd.id] }, + device.forwarding_destination_id + ), label: t(:device_form_forwarding_destination_label), include_blank: "Default" %> <% end %> <%= form.fields_for :postprocessing, device.postprocessing || Postprocessing.new do |fp| %> <%= fp.text_field :hardware_url, label: t(:device_form_hardware_url_label), help: t(:device_form_postprocessing_blurb_html) %> diff --git a/config/locales/views/devices/en.yml b/config/locales/views/devices/en.yml index 7e10966cb..745a4bae1 100644 --- a/config/locales/views/devices/en.yml +++ b/config/locales/views/devices/en.yml @@ -26,6 +26,7 @@ en: device_form_is_private_label: "Make this kit private" device_form_precise_location_label: "Enable precise location" device_form_enable_forwarding_label: "Enable MQTT forwarding" + device_form_forwarding_destination_label: "Destination for MQTT forwarding" device_form_notifications_subhead: "Notifications" device_form_notifications_blurb: "Get emails when the following events occur:" device_form_notify_low_battery_label: "Battery level drops below 15%" diff --git a/db/migrate/20250715180503_add_forwarding_destinations.rb b/db/migrate/20250715180503_add_forwarding_destinations.rb new file mode 100644 index 000000000..8c328eb4f --- /dev/null +++ b/db/migrate/20250715180503_add_forwarding_destinations.rb @@ -0,0 +1,14 @@ +class AddForwardingDestinations < ActiveRecord::Migration[6.1] + def change + create_table :forwarding_destinations do |t| + t.string :name, null: false + t.timestamps + end + + add_index :forwarding_destinations, :name, unique: true + + change_table :devices do |t| + t.belongs_to :forwarding_destination, index: true, null: true, foreign_key: true + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 097d22cd4..90b413b01 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2025_05_05_081245) do +ActiveRecord::Schema.define(version: 2025_07_15_180503) do # These are extensions that must be enabled in order to support this database enable_extension "adminpack" @@ -109,7 +109,9 @@ t.string "hardware_slug_override" t.boolean "precise_location", default: true, null: false t.boolean "enable_forwarding", default: false, null: false + t.bigint "forwarding_destination_id" t.index ["device_token"], name: "index_devices_on_device_token", unique: true + t.index ["forwarding_destination_id"], name: "index_devices_on_forwarding_destination_id" t.index ["geohash"], name: "index_devices_on_geohash" t.index ["last_reading_at"], name: "index_devices_on_last_reading_at" t.index ["owner_id"], name: "index_devices_on_owner_id" @@ -150,6 +152,13 @@ t.index ["owner_id"], name: "index_experiments_on_owner_id" end + create_table "forwarding_destinations", force: :cascade do |t| + t.string "name", null: false + t.datetime "created_at", precision: 6, null: false + t.datetime "updated_at", precision: 6, null: false + t.index ["name"], name: "index_forwarding_destinations_on_name", unique: true + end + create_table "friendly_id_slugs", id: :serial, force: :cascade do |t| t.string "slug", null: false t.integer "sluggable_id", null: false @@ -346,6 +355,7 @@ add_foreign_key "api_tokens", "users", column: "owner_id" add_foreign_key "components", "devices" add_foreign_key "components", "sensors" + add_foreign_key "devices", "forwarding_destinations" add_foreign_key "devices_experiments", "devices" add_foreign_key "devices_experiments", "experiments" add_foreign_key "devices_tags", "devices"