-
Notifications
You must be signed in to change notification settings - Fork 106
Separating application.properties into different files based on profile #563
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
ishish07
wants to merge
34
commits into
openrewrite:main
Choose a base branch
from
ishish07:feature/SeparateApplicationProperties
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
34 commits
Select commit
Hold shift + click to select a range
1b36230
First draft of changes
ishish07 cb8dbe4
Added test to ensure application.properties doesn't change if no addi…
ishish07 ad78269
Used Auto Formatter
ishish07 75673fa
More Formatting
ishish07 27b2398
More Formatting 2
ishish07 b390d3a
fixing edge case 1
ishish07 26f3c19
unable to append to existing application-prof.properties
ishish07 e1eb520
testing for no application.properties
ishish07 b8c6239
removed cycles from tests
ishish07 cce5019
This code works!
ishish07 780250e
Forgot to format test file
ishish07 b1e4719
More formatting
ishish07 5b65012
Fixed weird spacing issue
ishish07 0cf74f9
Formatting and Code Cleanup
ishish07 e7398d3
Changed approach to create blank properties files and append to every…
ishish07 35ae8c7
Converted existingPropertiesFiles to Set of Strings
ishish07 825d68c
New properties files will go into same folder as application.properties
ishish07 46d75d0
Merge branch 'main' into feature/SeparateApplicationProperties
timtebeek 5aef7ae
Apply suggestions from code review
timtebeek 87e7146
Add missing braces, language hints and apply formatter
timtebeek 6c3cdd7
Minor polish
timtebeek 4f23297
Add test showing multi module project structure
timtebeek fbca498
Merge branch 'main' into feature/SeparateApplicationProperties
timtebeek 9363b0f
Merge branch 'openrewrite:main' into feature/SeparateApplicationPrope…
ishish07 3631135
Merge branch 'openrewrite:main' into feature/SeparateApplicationPrope…
ishish07 798caa3
Works for multi-modular projects
ishish07 ef99fbb
responding to bot's comment
ishish07 85a9aa1
Merge branch 'openrewrite:main' into feature/SeparateApplicationPrope…
ishish07 dc7d1de
Merge branch 'openrewrite:main' into feature/SeparateApplicationPrope…
ishish07 967e809
using JavaProject
ishish07 c90923d
Merge branch 'main' into feature/SeparateApplicationProperties
timtebeek 2549403
Apply suggestions from code review
timtebeek a6c0f45
Merge branch 'main' into feature/SeparateApplicationProperties
timtebeek cceb6e3
Move test class & method
timtebeek File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
240 changes: 240 additions & 0 deletions
240
src/main/java/org/openrewrite/java/spring/SeparateApplicationPropertiesByProfile.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,240 @@ | ||
/* | ||
* Copyright 2024 the original author or authors. | ||
* <p> | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* <p> | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* <p> | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package org.openrewrite.java.spring; | ||
|
||
import lombok.EqualsAndHashCode; | ||
import lombok.Value; | ||
import org.jspecify.annotations.Nullable; | ||
import org.openrewrite.*; | ||
import org.openrewrite.java.marker.JavaProject; | ||
import org.openrewrite.marker.Markers; | ||
import org.openrewrite.properties.CreatePropertiesFile; | ||
import org.openrewrite.properties.PropertiesVisitor; | ||
import org.openrewrite.properties.tree.Properties; | ||
|
||
import java.util.*; | ||
import java.util.concurrent.atomic.AtomicBoolean; | ||
import static java.util.Collections.singletonList; | ||
import static java.util.stream.Collectors.toList; | ||
|
||
import java.util.stream.Stream; | ||
|
||
@Value | ||
@EqualsAndHashCode(callSuper = false) | ||
public class SeparateApplicationPropertiesByProfile extends ScanningRecipe<SeparateApplicationPropertiesByProfile.Accumulator> { | ||
|
||
@Override | ||
public String getDisplayName() { | ||
return "Separate `application.properties` by profile"; | ||
} | ||
|
||
@Override | ||
public String getDescription() { | ||
return "Separating `application.properties` into separate files based on profiles."; | ||
} | ||
|
||
@Override | ||
public Accumulator getInitialValue(ExecutionContext ctx) { | ||
return new Accumulator(); | ||
} | ||
|
||
@Override | ||
public TreeVisitor<?, ExecutionContext> getScanner(Accumulator acc) { | ||
return new TreeVisitor<Tree, ExecutionContext>() { | ||
@Override | ||
public @Nullable Tree visit(@Nullable Tree tree, ExecutionContext ctx) { | ||
if (!(tree instanceof Properties.File)) { | ||
return tree; | ||
} | ||
|
||
Properties.File propertyFile = (Properties.File) tree; | ||
Optional<JavaProject> javaProject = propertyFile.getMarkers().findFirst(JavaProject.class); | ||
if (!javaProject.isPresent()) { | ||
return tree; | ||
} | ||
|
||
String sourcePath = PathUtils.separatorsToUnix(propertyFile.getSourcePath().toString()); | ||
String[] pathArray = sourcePath.split("/"); | ||
|
||
// Get or create the module info using the JavaProject marker as the key | ||
ModulePropertyInfo moduleInfo = acc.moduleProperties.computeIfAbsent( | ||
javaProject.get(), | ||
k -> new ModulePropertyInfo() | ||
); | ||
|
||
if (moduleInfo.javaProject == null) { | ||
moduleInfo.javaProject = javaProject.get(); | ||
} | ||
|
||
if (propertyFile.getSourcePath().endsWith("application.properties")) { | ||
moduleInfo.pathToApplicationProperties = getPathToApplicationProperties(pathArray); | ||
moduleInfo.propertyFileContent = getNewApplicationPropertyFileInfo(propertyFile.getContent()); | ||
|
||
} | ||
|
||
if (propertyFile.getSourcePath().getFileName().toString().matches("application-[^/]+\\.properties")) { | ||
moduleInfo.fileNameToFilePath.put(pathArray[pathArray.length - 1], sourcePath); | ||
} | ||
|
||
return tree; | ||
} | ||
}; | ||
} | ||
|
||
|
||
|
||
@Override | ||
public Collection<? extends SourceFile> generate(Accumulator acc, ExecutionContext ctx) { | ||
Set<SourceFile> newApplicationPropertiesFiles = new HashSet<>(); | ||
|
||
// Change the loop to iterate over the entrySet to access the full module info | ||
for (ModulePropertyInfo moduleInfo : acc.moduleProperties.values()) { | ||
if (moduleInfo.propertyFileContent.isEmpty() || moduleInfo.javaProject == null) { | ||
continue; | ||
} | ||
|
||
for (Map.Entry<String, List<Properties.Content>> entry : moduleInfo.propertyFileContent.entrySet()) { | ||
if (!moduleInfo.fileNameToFilePath.containsKey(entry.getKey())) { | ||
|
||
// 1. Generate the new file as before | ||
SourceFile newFile = new CreatePropertiesFile( | ||
moduleInfo.pathToApplicationProperties + entry.getKey(), | ||
"", | ||
null | ||
).generate(new AtomicBoolean(true), ctx).iterator().next(); | ||
|
||
// 2. Get the stored project marker | ||
JavaProject projectMarker = moduleInfo.javaProject; | ||
|
||
// 3. Use withMarkers() to attach it to the new file | ||
SourceFile newFileWithMarker = newFile.withMarkers( | ||
// Markers.build() creates the container for our marker | ||
Markers.build(singletonList(projectMarker)) | ||
); | ||
|
||
// 4. Add the file *with the new marker* to our results | ||
newApplicationPropertiesFiles.add(newFileWithMarker); | ||
} | ||
} | ||
} | ||
|
||
return newApplicationPropertiesFiles; | ||
} | ||
|
||
@Override | ||
public TreeVisitor<?, ExecutionContext> getVisitor(Accumulator acc) { | ||
return new PropertiesVisitor<ExecutionContext>() { | ||
@Override | ||
public Properties visitFile(Properties.File file, ExecutionContext ctx) { | ||
Optional<JavaProject> javaProject = file.getMarkers().findFirst(JavaProject.class); | ||
if (!javaProject.isPresent()) { | ||
return file; | ||
} | ||
|
||
ModulePropertyInfo moduleInfo = acc.moduleProperties.get(javaProject.get()); | ||
if (moduleInfo == null || moduleInfo.propertyFileContent.isEmpty()) { | ||
return file; | ||
} | ||
|
||
String[] filePathArray = file.getSourcePath().toString().split("/"); | ||
String fileName = filePathArray[filePathArray.length - 1]; | ||
|
||
return fileName.matches("application.properties") ? | ||
deleteFromApplicationProperties(file) : | ||
appendToExistingPropertiesFile(file, moduleInfo.propertyFileContent.get(fileName)); | ||
} | ||
}; | ||
} | ||
|
||
private Properties appendToExistingPropertiesFile(Properties.File file, List<Properties.Content> contentToAppend) { | ||
return file.withContent( | ||
Stream.concat(file.getContent().stream(), contentToAppend.stream()). | ||
collect(toList())); | ||
} | ||
|
||
private Properties deleteFromApplicationProperties(Properties.File applicationProperties) { | ||
List<Properties.Content> newContent = new ArrayList<>(); | ||
for (Properties.Content c : applicationProperties.getContent()) { | ||
if (isSeparator(c)) { | ||
break; | ||
} | ||
newContent.add(c); | ||
} | ||
return applicationProperties.getContent().equals(newContent) ? applicationProperties : | ||
applicationProperties.withContent(newContent); | ||
} | ||
|
||
private Map<String, List<Properties.Content>> getNewApplicationPropertyFileInfo(List<Properties.Content> contentList) { | ||
Map<String, List<Properties.Content>> map = new HashMap<>(); | ||
int index = 0; | ||
while (index < contentList.size()) { | ||
if (isSeparator(contentList.get(index))) { | ||
List<Properties.Content> newContent = getContentForNewFile(contentList, ++index); | ||
if (!newContent.isEmpty() && newContent.get(0) instanceof Properties.Entry) { | ||
map.put("application-" + ((Properties.Entry) newContent.get(0)).getValue().getText() + ".properties", | ||
newContent.subList(1, newContent.size())); | ||
} | ||
} | ||
index++; | ||
} | ||
return map; | ||
} | ||
|
||
private List<Properties.Content> getContentForNewFile(List<Properties.Content> contentList, int index) { | ||
List<Properties.Content> list = new ArrayList<>(); | ||
while (index < contentList.size() && !isSeparator(contentList.get(index))) { | ||
if (contentList.get(index) instanceof Properties.Entry && | ||
"spring.config.activate.on-profile".equals | ||
(((Properties.Entry) contentList.get(index)).getKey())) { | ||
list.add(0, contentList.get(index)); | ||
} else { | ||
list.add(contentList.get(index)); | ||
} | ||
index++; | ||
} | ||
return list; | ||
} | ||
|
||
private String getPathToApplicationProperties(String[] pathArray) { | ||
return pathArray.length == 1 ? "" : String.join("/", Arrays.copyOfRange(pathArray, 0, pathArray.length - 1)) + "/"; | ||
} | ||
|
||
private boolean isSeparator(Properties.Content c) { | ||
return c instanceof Properties.Comment && | ||
"---".equals(((Properties.Comment) c).getMessage()) && | ||
((((Properties.Comment) c).getDelimiter() == | ||
Properties.Comment.Delimiter.valueOf("HASH_TAG")) || | ||
|
||
((Properties.Comment) c).getDelimiter() == | ||
Properties.Comment.Delimiter.valueOf("EXCLAMATION_MARK")); | ||
} | ||
|
||
public static class Accumulator { | ||
// Map from a module's JavaProject marker to its property file info | ||
Map<JavaProject, ModulePropertyInfo> moduleProperties = new HashMap<>(); | ||
} | ||
|
||
|
||
public static class ModulePropertyInfo { | ||
String pathToApplicationProperties = ""; | ||
Map<String, String> fileNameToFilePath = new HashMap<>(); | ||
Map<String, List<Properties.Content>> propertyFileContent = new HashMap<>(); | ||
timtebeek marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
@Nullable | ||
JavaProject javaProject; | ||
} | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.