diff --git a/spring-boot-project/spring-boot-properties-migrator/src/main/java/org/springframework/boot/context/properties/migrator/PropertiesMigrationReporter.java b/spring-boot-project/spring-boot-properties-migrator/src/main/java/org/springframework/boot/context/properties/migrator/PropertiesMigrationReporter.java index d8d99d2c64ed..298f51610e38 100644 --- a/spring-boot-project/spring-boot-properties-migrator/src/main/java/org/springframework/boot/context/properties/migrator/PropertiesMigrationReporter.java +++ b/spring-boot-project/spring-boot-properties-migrator/src/main/java/org/springframework/boot/context/properties/migrator/PropertiesMigrationReporter.java @@ -25,6 +25,7 @@ import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty; import org.springframework.boot.configurationmetadata.ConfigurationMetadataRepository; +import org.springframework.boot.configurationmetadata.Deprecation; import org.springframework.boot.context.properties.source.ConfigurationProperty; import org.springframework.boot.context.properties.source.ConfigurationPropertyName; import org.springframework.boot.context.properties.source.ConfigurationPropertySource; @@ -32,6 +33,7 @@ import org.springframework.boot.env.OriginTrackedMapPropertySource; import org.springframework.boot.origin.OriginTrackedValue; import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.MapPropertySource; import org.springframework.core.env.PropertySource; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; @@ -105,10 +107,50 @@ private Map> getMatchingProperties( result.add(name, new PropertyMigration(configurationProperty, metadata, determineReplacementMetadata(metadata))); } + getMatchingPropertiesForMapEntries(name, source, metadata, result); })); return result; } + private void getMatchingPropertiesForMapEntries(String name, ConfigurationPropertySource source, ConfigurationMetadataProperty metadata, MultiValueMap result) { + if (source.getUnderlyingSource() instanceof MapPropertySource && name.equals(metadata.getName())) { + MapPropertySource mapSource = ((MapPropertySource) source.getUnderlyingSource()); + for (String propertyName: mapSource.getPropertyNames()) { + ConfigurationProperty configurationProperty = source.getConfigurationProperty(ConfigurationPropertyName.of(propertyName)); + if (configurationProperty != null) { + ConfigurationMetadataProperty entryMetadata = generateMetadataForMapEntry(propertyName, metadata); + result.add(name, + new PropertyMigration(configurationProperty, entryMetadata, determineReplacementMetadata(entryMetadata))); + } + } + } + } + + private ConfigurationMetadataProperty generateMetadataForMapEntry(String propertyName, ConfigurationMetadataProperty mapMetadata) { + ConfigurationMetadataProperty newMetadata = new ConfigurationMetadataProperty(); + newMetadata.setName(propertyName); + newMetadata.setId(propertyName); + String entryName = propertyName.substring(propertyName.lastIndexOf(".")); + Deprecation oldDeprecation = mapMetadata.getDeprecation(); + if (oldDeprecation != null) { + Deprecation deprecation = new Deprecation(); + deprecation.setLevel(oldDeprecation.getLevel()); + deprecation.setReason(oldDeprecation.getReason()); + deprecation.setShortReason(oldDeprecation.getShortReason()); + deprecation.setReplacement(oldDeprecation.getReplacement() + entryName); + newMetadata.setDeprecation(deprecation); + } + + String type = mapMetadata.getType(); + if (type.startsWith(Map.class.getName())) { + int lastComma = type.lastIndexOf(','); + if (lastComma != -1) { + newMetadata.setType(type.substring(lastComma + 1, type.length() - 1).trim()); + } + } + return newMetadata; + } + private ConfigurationMetadataProperty determineReplacementMetadata(ConfigurationMetadataProperty metadata) { String replacementId = metadata.getDeprecation().getReplacement(); if (StringUtils.hasText(replacementId)) { diff --git a/spring-boot-project/spring-boot-properties-migrator/src/test/java/org/springframework/boot/context/properties/migrator/PropertiesMigrationReporterTests.java b/spring-boot-project/spring-boot-properties-migrator/src/test/java/org/springframework/boot/context/properties/migrator/PropertiesMigrationReporterTests.java index 4fed9c1902fc..076f416c8a8b 100644 --- a/spring-boot-project/spring-boot-properties-migrator/src/test/java/org/springframework/boot/context/properties/migrator/PropertiesMigrationReporterTests.java +++ b/spring-boot-project/spring-boot-properties-migrator/src/test/java/org/springframework/boot/context/properties/migrator/PropertiesMigrationReporterTests.java @@ -144,6 +144,30 @@ void invalidReplacementHandled() throws IOException { assertThat(report).doesNotContain("null"); } + @Test + void detectDeprecatedPropertyInMap() { + MutablePropertySources propertySources = this.environment.getPropertySources(); + Map content = new LinkedHashMap<>(); + content.put("status.http-mapping.down", 500); + content.put("status.http-mapping.out-of-service", 503); + content.put("status.http-mapping.warning", 500); + propertySources.addFirst(new MapPropertySource("status.http-mapping", content)); + assertThat(propertySources).hasSize(2); + + String report = createWarningReport(loadRepository("metadata/map-deprecated-metadata.json")); + assertThat(report).isNotNull(); + assertThat(report).contains("Property source 'status.http-mapping'", "Key: status.http-mapping.down", + "Replacement: endpoint.status.http-mapping.down", "Key: status.http-mapping.out-of-service", + "Replacement: endpoint.status.http-mapping.out-of-service", "Key: status.http-mapping.warning", + "Replacement: endpoint.status.http-mapping.warning"); + assertThat(mapToNames(propertySources)).containsExactly("migrate-status.http-mapping", "status.http-mapping", "mockProperties"); + PropertySource propertySource = propertySources.get("migrate-status.http-mapping"); + assertThat(propertySource).isNotNull(); + assertMappedProperty(propertySource, "endpoint.status.http-mapping.down", 500, null); + assertMappedProperty(propertySource, "endpoint.status.http-mapping.out-of-service", 503, null); + assertMappedProperty(propertySource, "endpoint.status.http-mapping.warning", 500, null); + } + private List mapToNames(PropertySources sources) { List names = new ArrayList<>(); for (PropertySource source : sources) { diff --git a/spring-boot-project/spring-boot-properties-migrator/src/test/resources/metadata/map-deprecated-metadata.json b/spring-boot-project/spring-boot-properties-migrator/src/test/resources/metadata/map-deprecated-metadata.json new file mode 100644 index 000000000000..3e2356d1a71b --- /dev/null +++ b/spring-boot-project/spring-boot-properties-migrator/src/test/resources/metadata/map-deprecated-metadata.json @@ -0,0 +1,17 @@ +{ + "properties": [ + { + "name": "status.http-mapping", + "type": "java.util.Map", + "deprecated": true, + "deprecation": { + "replacement": "endpoint.status.http-mapping" + } + }, + { + "name": "endpoint.status.http-mapping", + "type": "java.util.Map", + "description": "" + } + ] +} \ No newline at end of file