From 0b1391d1c2dec37bd350f485414aed1cb9779bf6 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Wed, 13 May 2026 14:56:44 +0000 Subject: [PATCH] Add region property to site and device metadata - Update schema/site_metadata.json to include region. - Update schema/model_system.json to include region under location. - Regenerate Java schema classes. - Update Registrar and LocalDevice to automatically copy region from site metadata to device metadata if unset during normalization. - Add unit tests in LocalDeviceTest to verify the propagation logic. Co-authored-by: noursaidi <9341216+noursaidi@users.noreply.github.com> --- gencode/java/udmi/schema/Location.java | 11 ++++++- gencode/java/udmi/schema/SiteMetadata.java | 11 ++++++- schema/model_system.json | 8 +++++ schema/site_metadata.json | 8 +++++ .../daq/mqtt/registrar/LocalDevice.java | 12 +++++++- .../google/daq/mqtt/registrar/Registrar.java | 3 +- .../daq/mqtt/registrar/LocalDeviceTest.java | 29 +++++++++++++++++++ 7 files changed, 78 insertions(+), 4 deletions(-) diff --git a/gencode/java/udmi/schema/Location.java b/gencode/java/udmi/schema/Location.java index 513e65fb63..493da2dd12 100644 --- a/gencode/java/udmi/schema/Location.java +++ b/gencode/java/udmi/schema/Location.java @@ -14,6 +14,7 @@ @JsonInclude(JsonInclude.Include.NON_NULL) @JsonPropertyOrder({ "site", + "region", "panel", "section", "room", @@ -32,6 +33,13 @@ public class Location { @JsonProperty("site") @JsonPropertyDescription("The site name according to the site model in which the device is installed in") public String site; + /** + * The region according to the site model in which the device is installed in + * + */ + @JsonProperty("region") + @JsonPropertyDescription("The region according to the site model in which the device is installed in") + public String region; /** * The reference of the panel where the device is installed in * @@ -70,6 +78,7 @@ public int hashCode() { result = ((result* 31)+((this.coordinates == null)? 0 :this.coordinates.hashCode())); result = ((result* 31)+((this.section == null)? 0 :this.section.hashCode())); result = ((result* 31)+((this.position == null)? 0 :this.position.hashCode())); + result = ((result* 31)+((this.region == null)? 0 :this.region.hashCode())); result = ((result* 31)+((this.panel == null)? 0 :this.panel.hashCode())); result = ((result* 31)+((this.floor == null)? 0 :this.floor.hashCode())); result = ((result* 31)+((this.room == null)? 0 :this.room.hashCode())); @@ -85,7 +94,7 @@ public boolean equals(Object other) { return false; } Location rhs = ((Location) other); - return (((((((((this.site == rhs.site)||((this.site!= null)&&this.site.equals(rhs.site)))&&((this.floor_seq == rhs.floor_seq)||((this.floor_seq!= null)&&this.floor_seq.equals(rhs.floor_seq))))&&((this.coordinates == rhs.coordinates)||((this.coordinates!= null)&&this.coordinates.equals(rhs.coordinates))))&&((this.section == rhs.section)||((this.section!= null)&&this.section.equals(rhs.section))))&&((this.position == rhs.position)||((this.position!= null)&&this.position.equals(rhs.position))))&&((this.panel == rhs.panel)||((this.panel!= null)&&this.panel.equals(rhs.panel))))&&((this.floor == rhs.floor)||((this.floor!= null)&&this.floor.equals(rhs.floor))))&&((this.room == rhs.room)||((this.room!= null)&&this.room.equals(rhs.room)))); + return ((((((((((this.site == rhs.site)||((this.site!= null)&&this.site.equals(rhs.site)))&&((this.floor_seq == rhs.floor_seq)||((this.floor_seq!= null)&&this.floor_seq.equals(rhs.floor_seq))))&&((this.coordinates == rhs.coordinates)||((this.coordinates!= null)&&this.coordinates.equals(rhs.coordinates))))&&((this.section == rhs.section)||((this.section!= null)&&this.section.equals(rhs.section))))&&((this.position == rhs.position)||((this.position!= null)&&this.position.equals(rhs.position))))&&((this.region == rhs.region)||((this.region!= null)&&this.region.equals(rhs.region))))&&((this.panel == rhs.panel)||((this.panel!= null)&&this.panel.equals(rhs.panel))))&&((this.floor == rhs.floor)||((this.floor!= null)&&this.floor.equals(rhs.floor))))&&((this.room == rhs.room)||((this.room!= null)&&this.room.equals(rhs.room)))); } } diff --git a/gencode/java/udmi/schema/SiteMetadata.java b/gencode/java/udmi/schema/SiteMetadata.java index 492274739d..f4e0a9a978 100644 --- a/gencode/java/udmi/schema/SiteMetadata.java +++ b/gencode/java/udmi/schema/SiteMetadata.java @@ -22,6 +22,7 @@ "timestamp", "version", "site", + "region", "name", "tags", "strict_warnings", @@ -55,6 +56,13 @@ public class SiteMetadata { @JsonProperty("site") @JsonPropertyDescription("Identifier for the site or building") public java.lang.String site; + /** + * Region for the site + * + */ + @JsonProperty("region") + @JsonPropertyDescription("Region for the site") + public java.lang.String region; /** * Name of the site or building * @@ -111,6 +119,7 @@ public int hashCode() { result = ((result* 31)+((this.location == null)? 0 :this.location.hashCode())); result = ((result* 31)+((this.links == null)? 0 :this.links.hashCode())); result = ((result* 31)+((this.externals == null)? 0 :this.externals.hashCode())); + result = ((result* 31)+((this.region == null)? 0 :this.region.hashCode())); result = ((result* 31)+((this.version == null)? 0 :this.version.hashCode())); result = ((result* 31)+((this.parameters == null)? 0 :this.parameters.hashCode())); result = ((result* 31)+((this.strict_warnings == null)? 0 :this.strict_warnings.hashCode())); @@ -128,7 +137,7 @@ public boolean equals(Object other) { return false; } SiteMetadata rhs = ((SiteMetadata) other); - return (((((((((((this.site == rhs.site)||((this.site!= null)&&this.site.equals(rhs.site)))&&((this.name == rhs.name)||((this.name!= null)&&this.name.equals(rhs.name))))&&((this.location == rhs.location)||((this.location!= null)&&this.location.equals(rhs.location))))&&((this.links == rhs.links)||((this.links!= null)&&this.links.equals(rhs.links))))&&((this.externals == rhs.externals)||((this.externals!= null)&&this.externals.equals(rhs.externals))))&&((this.version == rhs.version)||((this.version!= null)&&this.version.equals(rhs.version))))&&((this.parameters == rhs.parameters)||((this.parameters!= null)&&this.parameters.equals(rhs.parameters))))&&((this.strict_warnings == rhs.strict_warnings)||((this.strict_warnings!= null)&&this.strict_warnings.equals(rhs.strict_warnings))))&&((this.timestamp == rhs.timestamp)||((this.timestamp!= null)&&this.timestamp.equals(rhs.timestamp))))&&((this.tags == rhs.tags)||((this.tags!= null)&&this.tags.equals(rhs.tags)))); + return ((((((((((((this.site == rhs.site)||((this.site!= null)&&this.site.equals(rhs.site)))&&((this.name == rhs.name)||((this.name!= null)&&this.name.equals(rhs.name))))&&((this.location == rhs.location)||((this.location!= null)&&this.location.equals(rhs.location))))&&((this.links == rhs.links)||((this.links!= null)&&this.links.equals(rhs.links))))&&((this.externals == rhs.externals)||((this.externals!= null)&&this.externals.equals(rhs.externals))))&&((this.region == rhs.region)||((this.region!= null)&&this.region.equals(rhs.region))))&&((this.version == rhs.version)||((this.version!= null)&&this.version.equals(rhs.version))))&&((this.parameters == rhs.parameters)||((this.parameters!= null)&&this.parameters.equals(rhs.parameters))))&&((this.strict_warnings == rhs.strict_warnings)||((this.strict_warnings!= null)&&this.strict_warnings.equals(rhs.strict_warnings))))&&((this.timestamp == rhs.timestamp)||((this.timestamp!= null)&&this.timestamp.equals(rhs.timestamp))))&&((this.tags == rhs.tags)||((this.tags!= null)&&this.tags.equals(rhs.tags)))); } } diff --git a/schema/model_system.json b/schema/model_system.json index 587e9e3cee..7cda546b98 100644 --- a/schema/model_system.json +++ b/schema/model_system.json @@ -79,6 +79,14 @@ "style": "bold" } }, + "region": { + "description": "The region according to the site model in which the device is installed in", + "type": "string", + "$presentation": { + "display": "show", + "style": "bold" + } + }, "panel": { "description": "The reference of the panel where the device is installed in", "type": "string", diff --git a/schema/site_metadata.json b/schema/site_metadata.json index d1562812b9..810b5751d1 100644 --- a/schema/site_metadata.json +++ b/schema/site_metadata.json @@ -34,6 +34,14 @@ "style": "bold" } }, + "region": { + "description": "Region for the site", + "type": "string", + "$presentation": { + "display": "show", + "style": "bold" + } + }, "name": { "description": "Name of the site or building", "type": "string", diff --git a/validator/src/main/java/com/google/daq/mqtt/registrar/LocalDevice.java b/validator/src/main/java/com/google/daq/mqtt/registrar/LocalDevice.java index 84fa96a6d4..e7cd4e75a4 100644 --- a/validator/src/main/java/com/google/daq/mqtt/registrar/LocalDevice.java +++ b/validator/src/main/java/com/google/daq/mqtt/registrar/LocalDevice.java @@ -19,6 +19,7 @@ import static com.google.udmi.util.GeneralUtils.ifNotNullGet; import static com.google.udmi.util.GeneralUtils.ifNotNullThen; import static com.google.udmi.util.GeneralUtils.ifNotTrueThen; +import static com.google.udmi.util.GeneralUtils.ifNullThen; import static com.google.udmi.util.GeneralUtils.ifTrueThen; import static com.google.udmi.util.GeneralUtils.isTrue; import static com.google.udmi.util.GeneralUtils.writeString; @@ -94,8 +95,11 @@ import udmi.schema.Envelope.SubFolder; import udmi.schema.Envelope.SubType; import udmi.schema.GatewayModel; +import udmi.schema.Location; import udmi.schema.Metadata; import udmi.schema.PointPointsetModel; +import udmi.schema.SiteMetadata; +import udmi.schema.SystemModel; class LocalDevice implements SiteDevice { @@ -852,9 +856,15 @@ public LocalDevice duplicate(String newId) { return new LocalDevice(siteModel, newId, schemas, generation, deviceKind); } - public void preprocessMetadata() { + public void preprocessMetadata(SiteMetadata siteMetadata) { ifTrueWarn(catchToNull(() -> metadata.cloud.config.static_file) != null, "Disallowed cloud.config.static_file defined"); + + ifNotNullThen(siteMetadata.region, region -> { + ifNullThen(metadata.system, () -> metadata.system = new SystemModel()); + ifNullThen(metadata.system.location, () -> metadata.system.location = new Location()); + ifNullThen(metadata.system.location.region, () -> metadata.system.location.region = region); + }); } private void ifTrueWarn(boolean condition, String message) { diff --git a/validator/src/main/java/com/google/daq/mqtt/registrar/Registrar.java b/validator/src/main/java/com/google/daq/mqtt/registrar/Registrar.java index 4bfd5813be..4cef9d0701 100644 --- a/validator/src/main/java/com/google/daq/mqtt/registrar/Registrar.java +++ b/validator/src/main/java/com/google/daq/mqtt/registrar/Registrar.java @@ -1461,9 +1461,10 @@ private Set proxiedChildren(String gatewayId) { } private void preprocessDeviceMetadata(Map workingDevices) { + SiteMetadata siteMetadata = getSiteMetadata(); workingDevices.values().forEach(localDevice -> { try { - localDevice.preprocessMetadata(); + localDevice.preprocessMetadata(siteMetadata); } catch (ValidationError error) { throw new RuntimeException("While preprocessing metadata", error); } catch (Exception e) { diff --git a/validator/src/test/java/com/google/daq/mqtt/registrar/LocalDeviceTest.java b/validator/src/test/java/com/google/daq/mqtt/registrar/LocalDeviceTest.java index c97014a227..70443092a8 100644 --- a/validator/src/test/java/com/google/daq/mqtt/registrar/LocalDeviceTest.java +++ b/validator/src/test/java/com/google/daq/mqtt/registrar/LocalDeviceTest.java @@ -16,6 +16,7 @@ import java.util.Map; import org.junit.Test; import udmi.schema.Metadata; +import udmi.schema.SiteMetadata; /** * Unit tests for LocalDevice. @@ -56,4 +57,32 @@ private LocalDevice getTestInstance(String sitePath) { rawMetadata.gateway = null; return new LocalDevice(siteModel, DEVICE_ID, SCHEMAS, null, DeviceKind.SIMPLE); } + + @Test + public void preprocessMetadataRegion() { + LocalDevice localDevice = getTestInstance(EMPTY_DEFAULTS_SITE); + SiteMetadata siteMetadata = new SiteMetadata(); + siteMetadata.region = "test-region"; + + assertNull("device region is initially null", + catchToNull(() -> localDevice.getMetadata().system.location.region)); + + localDevice.preprocessMetadata(siteMetadata); + + assertEquals("device region is populated from site metadata", + "test-region", localDevice.getMetadata().system.location.region); + } + + @Test + public void preprocessMetadataRegionExisting() { + LocalDevice localDevice = getTestInstance(EMPTY_DEFAULTS_SITE); + localDevice.getMetadata().system.location.region = "existing-region"; + SiteMetadata siteMetadata = new SiteMetadata(); + siteMetadata.region = "test-region"; + + localDevice.preprocessMetadata(siteMetadata); + + assertEquals("existing device region is preserved", + "existing-region", localDevice.getMetadata().system.location.region); + } } \ No newline at end of file