From 2c079dc72b4a88eeade69f0c0487820abcc6e1f0 Mon Sep 17 00:00:00 2001 From: Ali Naqvi Date: Thu, 20 Nov 2025 11:11:17 +0800 Subject: [PATCH 1/3] feat: [PPT-2295] zone queries to support multiple parent_ids + fix to loki query --- shard.lock | 6 +- shard.override.yml | 2 +- spec/controllers/zones_spec.cr | 68 +++++++++++++++++++++ src/placeos-rest-api/controllers/modules.cr | 2 +- src/placeos-rest-api/controllers/zones.cr | 10 +-- 5 files changed, 79 insertions(+), 9 deletions(-) diff --git a/shard.lock b/shard.lock index 5be21aaf..a3774cb9 100644 --- a/shard.lock +++ b/shard.lock @@ -155,7 +155,7 @@ shards: neuroplastic: # Overridden git: https://github.com/place-labs/neuroplastic.git - version: 1.13.2+git.commit.2c156ab8ce688d676ca912b25ccdc631b265952c + version: 1.13.3+git.commit.b8549eb1e61a6f893ae08e219f53e2a5d48777ec office365: git: https://github.com/placeos/office365.git @@ -211,7 +211,7 @@ shards: placeos-frontend-loader: git: https://github.com/placeos/frontend-loader.git - version: 2.7.1+git.commit.ad484f46c8af671421f7f5b4704eccca7e30250e + version: 2.7.1+git.commit.d47bdbc18c04052f6eacd463d105716c8796e863 placeos-log-backend: git: https://github.com/place-labs/log-backend.git @@ -267,7 +267,7 @@ shards: search-ingest: git: https://github.com/placeos/search-ingest.git - version: 2.11.3+git.commit.89ff9805b666f6dbb55fed4b8d8f5d6cdb49edfa + version: 2.11.3+git.commit.8dabc64759d45a202393f6d000fd6ec05d50ec17 secrets-env: # Overridden git: https://github.com/spider-gazelle/secrets-env.git diff --git a/shard.override.yml b/shard.override.yml index 038bfa21..9b829914 100644 --- a/shard.override.yml +++ b/shard.override.yml @@ -20,7 +20,7 @@ dependencies: neuroplastic: github: place-labs/neuroplastic - commit: 2c156ab8ce688d676ca912b25ccdc631b265952c + commit: b8549eb1e61a6f893ae08e219f53e2a5d48777ec redis: github: stefanwille/crystal-redis \ No newline at end of file diff --git a/spec/controllers/zones_spec.cr b/spec/controllers/zones_spec.cr index 6fdc5cd1..ee7c65c8 100644 --- a/spec/controllers/zones_spec.cr +++ b/spec/controllers/zones_spec.cr @@ -6,6 +6,74 @@ module PlaceOS::Api describe "index", tags: "search" do Spec.test_base_index(klass: Model::Zone, controller_klass: Zones) + + it "filters by single parent_id" do + parent = Model::Generator.zone.save! + child1 = Model::Generator.zone + child1.parent_id = parent.id + child1.save! + child2 = Model::Generator.zone + child2.parent_id = parent.id + child2.save! + + sleep 1.second + refresh_elastic(Model::Zone.table_name) + + params = HTTP::Params.encode({"parent_id" => parent.id.as(String)}) + path = "#{Zones.base_route}?#{params}" + result = client.get(path, headers: Spec::Authentication.headers) + + result.success?.should be_true + zones = Array(Hash(String, JSON::Any)).from_json(result.body) + zone_ids = zones.map { |z| z["id"].as_s } + zone_ids.should contain(child1.id) + zone_ids.should contain(child2.id) + + parent.destroy + child1.destroy + child2.destroy + end + + it "filters by multiple parent_ids (comma-separated)" do + parent1 = Model::Generator.zone.save! + parent2 = Model::Generator.zone.save! + parent3 = Model::Generator.zone.save! + + child1 = Model::Generator.zone + child1.parent_id = parent1.id + child1.save! + + child2 = Model::Generator.zone + child2.parent_id = parent2.id + child2.save! + + child3 = Model::Generator.zone + child3.parent_id = parent3.id + child3.save! + + sleep 1.second + refresh_elastic(Model::Zone.table_name) + + # Query for children of parent1 and parent2 (should not include child3) + parent_ids = "#{parent1.id},#{parent2.id}" + params = HTTP::Params.encode({"parent_id" => parent_ids}) + path = "#{Zones.base_route}?#{params}" + result = client.get(path, headers: Spec::Authentication.headers) + + result.success?.should be_true + zones = Array(Hash(String, JSON::Any)).from_json(result.body) + zone_ids = zones.map { |z| z["id"].as_s } + zone_ids.should contain(child1.id) + zone_ids.should contain(child2.id) + zone_ids.should_not contain(child3.id) + + parent1.destroy + parent2.destroy + parent3.destroy + child1.destroy + child2.destroy + child3.destroy + end end describe "tags", tags: "search" do diff --git a/src/placeos-rest-api/controllers/modules.cr b/src/placeos-rest-api/controllers/modules.cr index 8a7b9157..d2abd46b 100644 --- a/src/placeos-rest-api/controllers/modules.cr +++ b/src/placeos-rest-api/controllers/modules.cr @@ -246,7 +246,7 @@ module PlaceOS::Api client = Loki::Client.from_env labels = client.list_labels.data stream = labels.try &.includes?("container") ? "container" : "app" - query = %({#{stream}="core"} | logfmt | source = "#{current_module.id}" | level = "[E]") + query = %({#{stream}="core"} | source = "#{current_module.id}" |~ "(?i)exception" | level = "ERROR|[E]") results = client.query_range(query, 20, error_timestamp - 1.hour, error_timestamp, Loki::Direction::Backward) entries = Array(String).new results.response_data.result.as(Loki::Model::Streams).each do |res_stream| diff --git a/src/placeos-rest-api/controllers/zones.cr b/src/placeos-rest-api/controllers/zones.cr index 405451ee..9a4c56d6 100644 --- a/src/placeos-rest-api/controllers/zones.cr +++ b/src/placeos-rest-api/controllers/zones.cr @@ -96,7 +96,7 @@ module PlaceOS::Api # list the configured zones @[AC::Route::GET("/", converters: {tags: ConvertStringArray})] def index( - @[AC::Param::Info(description: "only return zones who have this zone as a parent", example: "zone-1234")] + @[AC::Param::Info(description: "only return zones who have this zone as a parent (supports comma-separated list)", example: "zone-1234,zone-5678")] parent_id : String? = nil, @[AC::Param::Info(description: "return zones with particular tags", example: "building,level")] tags : Array(String)? = nil, @@ -105,11 +105,13 @@ module PlaceOS::Api query = elastic.query(search_params) query.sort(NAME_SORT_ASC) - # Limit results to the children of this parent + # Limit results to the children of these parents (OR logic) if parent = parent_id - query.must({ - "parent_id" => [parent], + parent_ids = parent.split(',').map(&.strip).reject(&.empty?) + query.should({ + "parent_id" => parent_ids, }) + query.minimum_should_match(1) end # Limit results to zones containing the passed list of tags From ebc9e562512eca70070dbb55689ed70de22050b5 Mon Sep 17 00:00:00 2001 From: Ali Naqvi Date: Thu, 20 Nov 2025 12:04:27 +0800 Subject: [PATCH 2/3] refactor: endpoint + specs --- spec/controllers/zones_spec.cr | 4 ++-- src/placeos-rest-api/controllers/zones.cr | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/spec/controllers/zones_spec.cr b/spec/controllers/zones_spec.cr index ee7c65c8..1e53f8d4 100644 --- a/spec/controllers/zones_spec.cr +++ b/spec/controllers/zones_spec.cr @@ -25,7 +25,7 @@ module PlaceOS::Api result.success?.should be_true zones = Array(Hash(String, JSON::Any)).from_json(result.body) - zone_ids = zones.map { |z| z["id"].as_s } + zone_ids = zones.map(&.["id"].as_s) zone_ids.should contain(child1.id) zone_ids.should contain(child2.id) @@ -62,7 +62,7 @@ module PlaceOS::Api result.success?.should be_true zones = Array(Hash(String, JSON::Any)).from_json(result.body) - zone_ids = zones.map { |z| z["id"].as_s } + zone_ids = zones.map(&.["id"].as_s) zone_ids.should contain(child1.id) zone_ids.should contain(child2.id) zone_ids.should_not contain(child3.id) diff --git a/src/placeos-rest-api/controllers/zones.cr b/src/placeos-rest-api/controllers/zones.cr index 9a4c56d6..4019126e 100644 --- a/src/placeos-rest-api/controllers/zones.cr +++ b/src/placeos-rest-api/controllers/zones.cr @@ -94,10 +94,10 @@ module PlaceOS::Api ############################################################################################### # list the configured zones - @[AC::Route::GET("/", converters: {tags: ConvertStringArray})] + @[AC::Route::GET("/", converters: {tags: ConvertStringArray, parent_id: ConvertStringArray})] def index( @[AC::Param::Info(description: "only return zones who have this zone as a parent (supports comma-separated list)", example: "zone-1234,zone-5678")] - parent_id : String? = nil, + parent_id : Array(String)? = nil, @[AC::Param::Info(description: "return zones with particular tags", example: "building,level")] tags : Array(String)? = nil, ) : Array(::PlaceOS::Model::Zone) @@ -107,7 +107,7 @@ module PlaceOS::Api # Limit results to the children of these parents (OR logic) if parent = parent_id - parent_ids = parent.split(',').map(&.strip).reject(&.empty?) + parent_ids = parent.map(&.strip).reject(&.empty?) query.should({ "parent_id" => parent_ids, }) From dc6e746e56c7b2ace19b75d2e5f5d9f2b87c16b6 Mon Sep 17 00:00:00 2001 From: Stephen von Takach Date: Thu, 20 Nov 2025 15:44:02 +1100 Subject: [PATCH 3/3] simplify parent_id check --- src/placeos-rest-api/controllers/zones.cr | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/placeos-rest-api/controllers/zones.cr b/src/placeos-rest-api/controllers/zones.cr index 4019126e..db5e53cc 100644 --- a/src/placeos-rest-api/controllers/zones.cr +++ b/src/placeos-rest-api/controllers/zones.cr @@ -106,10 +106,9 @@ module PlaceOS::Api query.sort(NAME_SORT_ASC) # Limit results to the children of these parents (OR logic) - if parent = parent_id - parent_ids = parent.map(&.strip).reject(&.empty?) + if parent_id query.should({ - "parent_id" => parent_ids, + "parent_id" => parent_id, }) query.minimum_should_match(1) end