Merge branch 'staging' into issue-1582
commit
98a0abd7fa
|
@ -12,6 +12,7 @@ module Devices
|
|||
time :last_saw_mq
|
||||
time :last_ota
|
||||
time :last_ota_checkup
|
||||
boolean :needs_reset
|
||||
integer :mounted_tool_id, nils: true
|
||||
integer :ota_hour, nils: true
|
||||
end
|
||||
|
|
|
@ -10,13 +10,14 @@ module SensorReadings
|
|||
end
|
||||
|
||||
optional do
|
||||
time :read_at
|
||||
integer :mode,
|
||||
in: CeleryScriptSettingsBag::ALLOWED_PIN_MODES,
|
||||
default: CeleryScriptSettingsBag::DIGITAL
|
||||
end
|
||||
|
||||
def execute
|
||||
SensorReading.create!(inputs)
|
||||
SensorReading.create!(inputs.merge(read_at: read_at || Time.now))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,6 +6,7 @@ class DeviceSerializer < ApplicationSerializer
|
|||
:last_saw_mq,
|
||||
:mounted_tool_id,
|
||||
:name,
|
||||
:needs_reset,
|
||||
:ota_hour,
|
||||
:serial_number,
|
||||
:throttled_at,
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
class SensorReadingSerializer < ApplicationSerializer
|
||||
attributes :mode, :pin, :value, :x, :y, :z
|
||||
attributes :mode, :pin, :value, :x, :y, :z, :read_at
|
||||
# This is for legacy support reasons.
|
||||
# Very old sensor_readings will have a
|
||||
# read_at value of `nil`, so we pre-populate it
|
||||
# to `created_at` for the convinience of API users.
|
||||
def read_at
|
||||
object.read_at || object.created_at
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
class AddNeedsResetToDevices < ActiveRecord::Migration[6.0]
|
||||
def change
|
||||
add_column :devices,
|
||||
:needs_reset,
|
||||
:boolean,
|
||||
default: false
|
||||
end
|
||||
end
|
|
@ -0,0 +1,5 @@
|
|||
class AddReadAtToSensorReadings < ActiveRecord::Migration[6.0]
|
||||
def change
|
||||
add_column :sensor_readings, :read_at, :datetime, default: nil
|
||||
end
|
||||
end
|
|
@ -276,7 +276,8 @@ CREATE TABLE public.devices (
|
|||
mqtt_rate_limit_email_sent_at timestamp without time zone,
|
||||
last_ota timestamp without time zone,
|
||||
last_ota_checkup timestamp without time zone,
|
||||
ota_hour integer DEFAULT 3
|
||||
ota_hour integer DEFAULT 3,
|
||||
needs_reset boolean DEFAULT false
|
||||
);
|
||||
|
||||
|
||||
|
@ -663,6 +664,40 @@ CREATE SEQUENCE public.firmware_configs_id_seq
|
|||
ALTER SEQUENCE public.firmware_configs_id_seq OWNED BY public.firmware_configs.id;
|
||||
|
||||
|
||||
--
|
||||
-- Name: folders; Type: TABLE; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
CREATE TABLE public.folders (
|
||||
id bigint NOT NULL,
|
||||
device_id bigint NOT NULL,
|
||||
created_at timestamp(6) without time zone NOT NULL,
|
||||
updated_at timestamp(6) without time zone NOT NULL,
|
||||
color character varying(20) NOT NULL,
|
||||
name character varying(40) NOT NULL,
|
||||
parent_id integer
|
||||
);
|
||||
|
||||
|
||||
--
|
||||
-- Name: folders_id_seq; Type: SEQUENCE; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
CREATE SEQUENCE public.folders_id_seq
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
NO MAXVALUE
|
||||
CACHE 1;
|
||||
|
||||
|
||||
--
|
||||
-- Name: folders_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER SEQUENCE public.folders_id_seq OWNED BY public.folders.id;
|
||||
|
||||
|
||||
--
|
||||
-- Name: fragments; Type: TABLE; Schema: public; Owner: -
|
||||
--
|
||||
|
@ -841,7 +876,8 @@ CREATE TABLE public.sequences (
|
|||
kind character varying(280) DEFAULT 'sequence'::character varying,
|
||||
updated_at timestamp without time zone,
|
||||
created_at timestamp without time zone,
|
||||
migrated_nodes boolean DEFAULT false
|
||||
migrated_nodes boolean DEFAULT false,
|
||||
folder_id bigint
|
||||
);
|
||||
|
||||
|
||||
|
@ -1370,7 +1406,7 @@ CREATE VIEW public.resource_update_steps AS
|
|||
edge_nodes.kind,
|
||||
edge_nodes.value
|
||||
FROM public.edge_nodes
|
||||
WHERE (((edge_nodes.kind)::text = 'resource_type'::text) AND ((edge_nodes.value)::text = ANY ((ARRAY['"GenericPointer"'::character varying, '"ToolSlot"'::character varying, '"Plant"'::character varying])::text[])))
|
||||
WHERE (((edge_nodes.kind)::text = 'resource_type'::text) AND ((edge_nodes.value)::text = ANY (ARRAY[('"GenericPointer"'::character varying)::text, ('"ToolSlot"'::character varying)::text, ('"Plant"'::character varying)::text])))
|
||||
), resource_id AS (
|
||||
SELECT edge_nodes.primary_node_id,
|
||||
edge_nodes.kind,
|
||||
|
@ -1447,7 +1483,8 @@ CREATE TABLE public.sensor_readings (
|
|||
pin integer,
|
||||
created_at timestamp without time zone NOT NULL,
|
||||
updated_at timestamp without time zone NOT NULL,
|
||||
mode integer DEFAULT 0
|
||||
mode integer DEFAULT 0,
|
||||
read_at timestamp without time zone
|
||||
);
|
||||
|
||||
|
||||
|
@ -1649,7 +1686,8 @@ CREATE TABLE public.users (
|
|||
agreed_to_terms_at timestamp without time zone,
|
||||
confirmation_sent_at timestamp without time zone,
|
||||
unconfirmed_email character varying,
|
||||
inactivity_warning_sent_at timestamp without time zone
|
||||
inactivity_warning_sent_at timestamp without time zone,
|
||||
inactivity_warning_count integer
|
||||
);
|
||||
|
||||
|
||||
|
@ -1887,6 +1925,13 @@ ALTER TABLE ONLY public.fbos_configs ALTER COLUMN id SET DEFAULT nextval('public
|
|||
ALTER TABLE ONLY public.firmware_configs ALTER COLUMN id SET DEFAULT nextval('public.firmware_configs_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: folders id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.folders ALTER COLUMN id SET DEFAULT nextval('public.folders_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: fragments id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
@ -2203,6 +2248,14 @@ ALTER TABLE ONLY public.firmware_configs
|
|||
ADD CONSTRAINT firmware_configs_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: folders folders_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.folders
|
||||
ADD CONSTRAINT folders_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: fragments fragments_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
|
@ -2575,6 +2628,13 @@ CREATE INDEX index_fbos_configs_on_device_id ON public.fbos_configs USING btree
|
|||
CREATE INDEX index_firmware_configs_on_device_id ON public.firmware_configs USING btree (device_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: index_folders_on_device_id; Type: INDEX; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
CREATE INDEX index_folders_on_device_id ON public.folders USING btree (device_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: index_fragments_on_device_id; Type: INDEX; Schema: public; Owner: -
|
||||
--
|
||||
|
@ -2904,6 +2964,13 @@ CREATE INDEX index_sequences_on_created_at ON public.sequences USING btree (crea
|
|||
CREATE INDEX index_sequences_on_device_id ON public.sequences USING btree (device_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: index_sequences_on_folder_id; Type: INDEX; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
CREATE INDEX index_sequences_on_folder_id ON public.sequences USING btree (folder_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: index_standard_pairs_on_arg_name_id; Type: INDEX; Schema: public; Owner: -
|
||||
--
|
||||
|
@ -3026,6 +3093,14 @@ ALTER TABLE ONLY public.pin_bindings
|
|||
ADD CONSTRAINT fk_rails_1f1c3b6979 FOREIGN KEY (device_id) REFERENCES public.devices(id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: folders fk_rails_58e285f76e; Type: FK CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.folders
|
||||
ADD CONSTRAINT fk_rails_58e285f76e FOREIGN KEY (parent_id) REFERENCES public.folders(id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: sensors fk_rails_92e56bf2fb; Type: FK CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
|
@ -3368,6 +3443,9 @@ INSERT INTO "schema_migrations" (version) VALUES
|
|||
('20190930202839'),
|
||||
('20191002125625'),
|
||||
('20191107170431'),
|
||||
('20191119204916');
|
||||
('20191119204916'),
|
||||
('20191203163621'),
|
||||
('20191219212755'),
|
||||
('20191220010646');
|
||||
|
||||
|
||||
|
|
|
@ -47,6 +47,7 @@ export function fakeSequence(): TaggedSequence {
|
|||
color: "red",
|
||||
name: "fake",
|
||||
kind: "sequence",
|
||||
folder_id: undefined,
|
||||
body: []
|
||||
});
|
||||
}
|
||||
|
@ -228,6 +229,7 @@ export function fakeSensorReading(): TaggedSensorReading {
|
|||
return fakeResource("SensorReading", {
|
||||
id: idCounter++,
|
||||
created_at: "2018-01-11T20:20:38.362Z",
|
||||
read_at: "2018-01-11T20:20:38.362Z",
|
||||
pin: 1,
|
||||
value: 0,
|
||||
mode: 0,
|
||||
|
|
|
@ -39,6 +39,7 @@ const tr0: TaggedResource = {
|
|||
"id": 23,
|
||||
"name": "Goto 0, 0, 0",
|
||||
"color": "gray",
|
||||
"folder_id": undefined,
|
||||
"body": [
|
||||
{
|
||||
"kind": "move_absolute",
|
||||
|
@ -366,6 +367,7 @@ const KIND_PRIORITY: ResourceLookupTable = {
|
|||
Sensor: 1,
|
||||
Tool: 1,
|
||||
Alert: 1,
|
||||
Folder: 1,
|
||||
PointGroup: 2,
|
||||
SensorReading: 2,
|
||||
Sequence: 2,
|
||||
|
@ -400,6 +402,7 @@ const blankSeq: TaggedSequence = {
|
|||
"body": {
|
||||
"id": undefined,
|
||||
"name": "Repair sequence",
|
||||
"folder_id": undefined,
|
||||
"color": "gray",
|
||||
"body": [],
|
||||
"args": {
|
||||
|
|
|
@ -39,6 +39,7 @@ describe("commitBulkEditor()", () => {
|
|||
id: sequence_id,
|
||||
name: "Test Sequence",
|
||||
color: "gray",
|
||||
folder_id: undefined,
|
||||
body: [{ kind: "wait", args: { milliseconds: 100 } }],
|
||||
args: { "locals": { kind: "scope_declaration", args: {} }, "version": 4 },
|
||||
kind: "sequence"
|
||||
|
|
|
@ -108,6 +108,7 @@ describe("in_use tracking at reducer level", () => {
|
|||
name: "Y",
|
||||
kind: "sequence",
|
||||
color: "blue",
|
||||
folder_id: undefined,
|
||||
args: { version: 8, locals: { kind: "scope_declaration", args: {} } }
|
||||
})[0];
|
||||
sequence.body.id = sequence_id;
|
||||
|
|
|
@ -27,6 +27,7 @@ describe("resource reducer", () => {
|
|||
const next = resourceReducer(state, overwrite(sequence, {
|
||||
kind: "sequence",
|
||||
name: "wow",
|
||||
folder_id: undefined,
|
||||
args: { version: -0, locals: { kind: "scope_declaration", args: {} } },
|
||||
body: [],
|
||||
color: "red"
|
||||
|
|
|
@ -10,6 +10,7 @@ describe("tagAllSteps()", () => {
|
|||
"body": {
|
||||
"id": 8,
|
||||
"name": "Goto 0, 0, 0",
|
||||
"folder_id": undefined,
|
||||
"color": "gray",
|
||||
"body": [
|
||||
{
|
||||
|
|
|
@ -63,6 +63,7 @@ export const emptyState = (): RestResources => {
|
|||
User: {},
|
||||
WebAppConfig: {},
|
||||
WebcamFeed: {},
|
||||
Folder: {}
|
||||
},
|
||||
byKindAndId: {},
|
||||
references: {},
|
||||
|
|
|
@ -15,6 +15,7 @@ describe("<AllSteps/>", () => {
|
|||
"body": sanitizeNodes({
|
||||
"id": 8,
|
||||
"name": "Goto 0, 0, 0",
|
||||
"folder_id": undefined,
|
||||
"color": "gray",
|
||||
"body": [
|
||||
{
|
||||
|
|
|
@ -36,6 +36,7 @@ describe("<TestButton/>", () => {
|
|||
"name": "Goto 0, 0, 0",
|
||||
"color": "gray",
|
||||
"body": [],
|
||||
"folder_id": undefined,
|
||||
"args": {
|
||||
"version": 4,
|
||||
"locals": { kind: "scope_declaration", args: {} },
|
||||
|
|
|
@ -40,6 +40,7 @@ describe("<InputDefault/>", () => {
|
|||
"name": "Goto 0, 0, 0",
|
||||
"color": "gray",
|
||||
"body": [step],
|
||||
"folder_id": undefined,
|
||||
"args": {
|
||||
"version": 4,
|
||||
"locals": { kind: "scope_declaration", args: {} },
|
||||
|
|
|
@ -91,6 +91,7 @@ const SequenceListItem = (props: SequenceListItemProps) =>
|
|||
|
||||
const emptySequenceBody = (seqCount: number): TaggedSequence["body"] => ({
|
||||
name: t("new sequence {{ num }}", { num: seqCount }),
|
||||
folder_id: undefined,
|
||||
args: {
|
||||
version: -999,
|
||||
locals: { kind: "scope_declaration", args: {} },
|
||||
|
|
|
@ -39,6 +39,7 @@ const seedSequence: TaggedSequence = {
|
|||
color: "red",
|
||||
name: "test",
|
||||
kind: "sequence",
|
||||
folder_id: undefined,
|
||||
args: {
|
||||
locals: { kind: "scope_declaration", args: {} },
|
||||
version: 9999
|
||||
|
|
20
package.json
20
package.json
|
@ -24,18 +24,18 @@
|
|||
"author": "farmbot.io",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/core": "7.7.4",
|
||||
"@blueprintjs/core": "3.22.0",
|
||||
"@babel/core": "7.7.5",
|
||||
"@blueprintjs/core": "3.22.2",
|
||||
"@blueprintjs/datetime": "3.15.1",
|
||||
"@blueprintjs/select": "3.11.2",
|
||||
"@types/enzyme": "3.10.3",
|
||||
"@types/enzyme": "3.10.4",
|
||||
"@types/jest": "24.0.23",
|
||||
"@types/lodash": "4.14.149",
|
||||
"@types/markdown-it": "0.0.9",
|
||||
"@types/moxios": "0.4.9",
|
||||
"@types/node": "12.12.14",
|
||||
"@types/node": "12.12.17",
|
||||
"@types/promise-timeout": "1.3.0",
|
||||
"@types/react": "16.9.14",
|
||||
"@types/react": "16.9.16",
|
||||
"@types/react-color": "3.0.1",
|
||||
"@types/react-dom": "16.9.4",
|
||||
"@types/react-redux": "7.1.5",
|
||||
|
@ -45,8 +45,8 @@
|
|||
"coveralls": "3.0.9",
|
||||
"enzyme": "3.10.0",
|
||||
"enzyme-adapter-react-16": "1.15.1",
|
||||
"farmbot": "8.3.1",
|
||||
"i18next": "19.0.1",
|
||||
"farmbot": "9.0.0",
|
||||
"i18next": "19.0.2",
|
||||
"install": "0.13.0",
|
||||
"lodash": "4.17.15",
|
||||
"markdown-it": "10.0.0",
|
||||
|
@ -54,7 +54,7 @@
|
|||
"moment": "2.24.0",
|
||||
"moxios": "0.4.0",
|
||||
"mqtt": "3.0.0",
|
||||
"npm": "6.13.1",
|
||||
"npm": "6.13.4",
|
||||
"parcel-bundler": "1.12.4",
|
||||
"promise-timeout": "1.3.0",
|
||||
"raf": "3.4.1",
|
||||
|
@ -75,14 +75,14 @@
|
|||
"ts-jest": "24.2.0",
|
||||
"ts-lint": "4.5.1",
|
||||
"tslint": "5.20.1",
|
||||
"typescript": "3.7.2",
|
||||
"typescript": "3.7.3",
|
||||
"which": "2.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"jest": "24.9.0",
|
||||
"jest-cli": "24.9.0",
|
||||
"jest-skipped-reporter": "0.0.5",
|
||||
"jshint": "2.10.3",
|
||||
"jshint": "2.11.0-rc1",
|
||||
"madge": "3.6.0"
|
||||
},
|
||||
"jest": {
|
||||
|
|
|
@ -7,11 +7,39 @@ describe Api::SensorReadingsController do
|
|||
let(:user) { FactoryBot.create(:user) }
|
||||
let (:reading) { FactoryBot.create(:sensor_reading, device: user.device) }
|
||||
|
||||
it "sets a default `read_at` value" do
|
||||
sign_in user
|
||||
before = SensorReading.count
|
||||
read_at = Time.now - 5.hours
|
||||
post :create,
|
||||
body: {
|
||||
pin: 13,
|
||||
value: 128,
|
||||
x: nil,
|
||||
y: 1,
|
||||
z: 2,
|
||||
mode: 1
|
||||
}.to_json,
|
||||
params: { format: :json }
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
expect(json[:created_at]).to eq(json[:read_at])
|
||||
end
|
||||
|
||||
it "makes a sensor reading" do
|
||||
sign_in user
|
||||
before = SensorReading.count
|
||||
read_at = Time.now - 5.hours
|
||||
post :create,
|
||||
body: { pin: 13, value: 128, x: nil, y: 1, z: 2, mode: 1 }.to_json,
|
||||
body: {
|
||||
pin: 13,
|
||||
value: 128,
|
||||
x: nil,
|
||||
y: 1,
|
||||
z: 2,
|
||||
mode: 1,
|
||||
read_at: read_at
|
||||
}.to_json,
|
||||
params: { format: :json }
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
|
@ -24,6 +52,7 @@ describe Api::SensorReadingsController do
|
|||
expect(json[:z]).to eq(2)
|
||||
expect(json[:pin]).to eq(13)
|
||||
expect(json[:mode]).to eq(1)
|
||||
expect(read_at.as_json.first(23)).to eq(json[:read_at].first(23))
|
||||
expect(before < SensorReading.count).to be_truthy
|
||||
end
|
||||
|
||||
|
|
|
@ -10,13 +10,17 @@ describe DashboardController do
|
|||
expect(response.status).to eq(200)
|
||||
end
|
||||
|
||||
it "renders the terms of service" do
|
||||
it "renders the front page" do
|
||||
get :front_page
|
||||
expect(response.status).to eq(200)
|
||||
# first entry in api_docs.md
|
||||
SmarfDoc.note("Documentation generated for the " +
|
||||
"[FarmBot Web App](https://github.com/FarmBot/Farmbot-Web-App).")
|
||||
end
|
||||
|
||||
it "renders the terms of service" do
|
||||
expect { get :main_app, params: { path: "nope.jpg" } }.to raise_error(ActionController::RoutingError)
|
||||
it "returns error on invalid path" do
|
||||
expect { get :main_app, params: { path: "nope.jpg" } }
|
||||
.to raise_error(ActionController::RoutingError)
|
||||
end
|
||||
|
||||
it "receives CSP violation reports (malformed JSON)" do
|
||||
|
@ -26,7 +30,7 @@ describe DashboardController do
|
|||
expect(json).to eq(problem: "Crashed while parsing report")
|
||||
end
|
||||
|
||||
it "receives CSP violation reports (malformed JSON)" do
|
||||
it "receives CSP violation reports (JSON)" do
|
||||
post :csp_reports, body: {}.to_json, params: { format: :json }
|
||||
expect(json).to eq({})
|
||||
end
|
||||
|
|
|
@ -7,10 +7,11 @@ describe Devices::Update do
|
|||
|
||||
it 'updates an existing device' do
|
||||
previous_name = device.name
|
||||
p = { last_saw_mq: Time.now.utc, name: "Farmbot" }
|
||||
p = { last_saw_mq: Time.now.utc, name: "Farmbot", needs_reset: true }
|
||||
Devices::Update.run!({device: device}, p)
|
||||
device.reload
|
||||
expect(device.name).to eq(p[:name])
|
||||
expect(device.needs_reset).to eq(p[:needs_reset])
|
||||
expect(device.last_saw_mq.hour).to eq(p[:last_saw_mq].hour)
|
||||
expect(device.last_saw_mq.min).to eq(p[:last_saw_mq].min)
|
||||
end
|
||||
|
|
|
@ -83,7 +83,9 @@ RSpec.configure do |config|
|
|||
config.order = "random"
|
||||
if ENV["DOCS"]
|
||||
config.after(:each, type: :controller) do
|
||||
SmarfDoc.run!(NiceResponse.new(request), response)
|
||||
if request.path.length > 0 || response.body.length > 0
|
||||
SmarfDoc.run!(NiceResponse.new(request), response)
|
||||
end
|
||||
end
|
||||
|
||||
config.after(:suite) do
|
||||
|
|
Loading…
Reference in New Issue