Skip to content

Commit 6e90295

Browse files
authored
Merge pull request #41 from KamilAdd-Byte/validator-pattern-dedicated-model
feat: validate and generated dedicated-meta-schema.json. Created endp…
2 parents 227806b + c229704 commit 6e90295

11 files changed

+367
-116
lines changed

src/main/java/pl/commit/craft/template/CommitCraftTemplateController.java

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
import io.swagger.v3.oas.annotations.Operation;
44
import io.swagger.v3.oas.annotations.responses.ApiResponse;
55
import lombok.RequiredArgsConstructor;
6+
import org.springframework.http.HttpStatus;
67
import org.springframework.http.ResponseEntity;
78
import org.springframework.web.bind.annotation.*;
89
import java.io.IOException;
10+
import java.util.Collections;
911
import java.util.List;
1012
import java.util.Map;
1113

@@ -30,18 +32,23 @@ public ResponseEntity<List<CommitCraftTemplate>> getAllTemplates() throws IOExce
3032
summary = "Create a dedicated commit template",
3133
description = "Creates a new dedicated commit template if the pattern and model scope are valid.",
3234
responses = {
33-
@ApiResponse(responseCode = "200", description = "Template added successfully"),
34-
@ApiResponse(responseCode = "400", description = "Template already exists")
35+
@ApiResponse(responseCode = "201", description = "Template created successfully"),
36+
@ApiResponse(responseCode = "400", description = "Invalid template format or template already exists")
3537
}
3638
)
3739
@PostMapping("/dedicated")
38-
public ResponseEntity<String> createDedicatedTemplate(@RequestBody CommitCraftTemplate template) throws IOException {
39-
boolean patternAndModelScope = CommitDedicatedTemplateValidator.validatePatternAndModelScope(template);
40-
if (patternAndModelScope) {
41-
commitTemplateService.createDedicatedTemplate(template);
42-
return ResponseEntity.ok("Template added successfully.");
43-
}
44-
return ResponseEntity.badRequest().body("Template already exists.");
40+
public ResponseEntity<Map<String, String>> createDedicatedTemplate(@RequestBody CommitCraftTemplate template) throws IOException {
41+
TemplateOperationResult result = commitTemplateService.createDedicatedTemplate(template);
42+
43+
if (result.success()) {
44+
return ResponseEntity
45+
.status(HttpStatus.CREATED)
46+
.body(Collections.singletonMap("message", result.message()));
47+
}
48+
49+
return ResponseEntity
50+
.status(HttpStatus.BAD_REQUEST)
51+
.body(Collections.singletonMap("error", result.message()));
4552
}
4653

4754
@Operation(
Lines changed: 61 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,79 @@
11
package pl.commit.craft.template;
22

33
import lombok.extern.slf4j.Slf4j;
4-
import java.util.Map;
4+
import java.util.HashSet;
55
import java.util.Set;
6+
import java.util.regex.Matcher;
7+
import java.util.regex.Pattern;
8+
import java.util.stream.Collectors;
69

710
@Slf4j
811
class CommitDedicatedTemplateValidator {
9-
private static final String PATTERN_VALIDATE_MODEL = "\\{(\\w+)\\}(-\\{(\\w+)\\})*";
12+
private static final String PLACEHOLDER_REGEX = "\\{(\\w+)\\}";
13+
private static final Pattern PLACEHOLDER_PATTERN = Pattern.compile(PLACEHOLDER_REGEX);
1014

11-
static boolean validatePatternAndModelScope(CommitCraftTemplate template) {
12-
log.info("Validating pattern and model scope starting");
13-
String pattern = template.getPattern();
14-
Map<String, Object> model = template.getModel();
15+
/**
16+
* For backward compatibility
17+
*/
18+
public static boolean validatePatternAndModelScope(CommitCraftTemplate template) {
19+
return validatePatternAndModelScopeDetailed(template).isValid();
20+
}
1521

16-
Set<String> modelKeys = model.keySet();
22+
/**
23+
* Validates that all keys in the model exist in the pattern and vice versa.
24+
*
25+
* @param template The commit template to validate
26+
* @return result of validation with details about any mismatches
27+
*/
28+
public static ValidationResult validatePatternAndModelScopeDetailed(CommitCraftTemplate template) {
29+
log.info("Validating pattern and model scope starting for template: {}", template.getName());
1730

18-
boolean matches = true;
19-
for (String key : modelKeys) {
20-
if (!pattern.contains(key)) {
21-
log.warn("Pattern is missing key: {}", key);
22-
matches = false;
23-
}
24-
}
31+
Set<String> modelKeys = template.getModel().keySet();
32+
Set<String> patternKeys = extractPlaceholdersFromPattern(template.getPattern());
2533

26-
String[] patternWords = pattern.split(PATTERN_VALIDATE_MODEL);
27-
for (String word : patternWords) {
28-
if (!modelKeys.contains(word)) {
29-
log.warn("Pattern contains an extra key not in the model: {}", word);
30-
matches = false;
31-
}
32-
}
34+
Set<String> missingInPattern = findMissingKeys(modelKeys, patternKeys);
35+
Set<String> extraInPattern = findMissingKeys(patternKeys, modelKeys);
36+
37+
boolean isValid = missingInPattern.isEmpty() && extraInPattern.isEmpty();
3338

34-
if (matches) {
39+
if (isValid) {
3540
log.info("Pattern matches the model keys.");
41+
return ValidationResult.valid();
3642
} else {
37-
log.warn("Pattern does not match the model keys.");
43+
if (!missingInPattern.isEmpty()) {
44+
log.warn("Pattern is missing keys: {}", missingInPattern);
45+
}
46+
if (!extraInPattern.isEmpty()) {
47+
log.warn("Pattern contains extra keys not in the model: {}", extraInPattern);
48+
}
49+
return ValidationResult.invalid(missingInPattern, extraInPattern);
3850
}
51+
}
52+
53+
54+
/**
55+
* Extracts all placeholder keys from the pattern string.
56+
*
57+
* @param pattern The pattern string containing placeholders
58+
* @return A set of placeholder keys
59+
*/
60+
private static Set<String> extractPlaceholdersFromPattern(String pattern) {
61+
Set<String> patternKeys = new HashSet<>();
62+
Matcher matcher = PLACEHOLDER_PATTERN.matcher(pattern);
63+
64+
while (matcher.find()) {
65+
patternKeys.add(matcher.group(1));
66+
}
67+
68+
return patternKeys;
69+
}
3970

40-
return matches;
71+
/**
72+
* Finds keys that are in the first set but not in the second set
73+
*/
74+
private static Set<String> findMissingKeys(Set<String> sourceKeys, Set<String> targetKeys) {
75+
return sourceKeys.stream()
76+
.filter(key -> !targetKeys.contains(key))
77+
.collect(Collectors.toSet());
4178
}
4279
}

src/main/java/pl/commit/craft/template/CommitTemplateService.java

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import com.fasterxml.jackson.databind.node.ArrayNode;
77
import com.fasterxml.jackson.databind.node.ObjectNode;
88
import lombok.RequiredArgsConstructor;
9+
import lombok.extern.slf4j.Slf4j;
910
import org.springframework.stereotype.Service;
1011
import java.io.File;
1112
import java.io.IOException;
@@ -14,6 +15,7 @@
1415
import java.util.List;
1516
import java.util.Map;
1617

18+
@Slf4j
1719
@Service
1820
@RequiredArgsConstructor
1921
class CommitTemplateService {
@@ -78,10 +80,57 @@ public List<CommitCraftTemplate> readTemplates() throws IOException {
7880
});
7981
}
8082

81-
public void createDedicatedTemplate(CommitCraftTemplate newTemplate) throws IOException {
82-
List<CommitCraftTemplate> templates = readTemplates();
83-
templates.add(newTemplate);
84-
saveTemplates(templates);
83+
/**
84+
* Creates a dedicated template after validating it.
85+
*
86+
* @param template The template to create
87+
* @return Result containing success/failure status and a message
88+
* @throws IOException If there's an issue accessing the template storage
89+
*/
90+
public TemplateOperationResult createDedicatedTemplate(CommitCraftTemplate template) throws IOException {
91+
ValidationResult validationResult =
92+
CommitDedicatedTemplateValidator.validatePatternAndModelScopeDetailed(template);
93+
94+
if (!validationResult.isValid()) {
95+
String errorMessage = validationResult.getErrorMessage();
96+
log.warn("Template validation failed: {}", errorMessage);
97+
return new TemplateOperationResult(false, errorMessage);
98+
}
99+
100+
if (templateExists(template.getName())) {
101+
log.warn("Template with name '{}' already exists", template.getName());
102+
return new TemplateOperationResult(false, "Template with name '" + template.getName() + "' already exists");
103+
}
104+
105+
saveTemplate(template);
106+
log.info("Template '{}' created successfully", template.getName());
107+
return new TemplateOperationResult(true, "Template created successfully");
108+
}
109+
110+
/**
111+
* Checks if a template with the given name already exists
112+
*
113+
* @param templateName Name of the template to check
114+
* @return true if the template exists, false otherwise
115+
* @throws IOException If there's an issue accessing the template storage
116+
*/
117+
private boolean templateExists(String templateName) throws IOException {
118+
List<CommitCraftTemplate> existingTemplates = getAllTemplates();
119+
return existingTemplates.stream()
120+
.anyMatch(template -> template.getName().equals(templateName));
121+
}
122+
123+
/**
124+
* Saves a new template to the dedicated templates file
125+
*
126+
* @param template The template to save
127+
* @throws IOException If there's an issue accessing or writing to the template storage
128+
*/
129+
private void saveTemplate(CommitCraftTemplate template) throws IOException {
130+
List<CommitCraftTemplate> existingTemplates = readTemplates();
131+
existingTemplates.add(template);
132+
saveTemplates(existingTemplates);
133+
log.debug("Template saved successfully: {}", template.getName());
85134
}
86135

87136
public void removeDedicatedTemplate(String dedicatedTemplateName) throws IOException {
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package pl.commit.craft.template;
2+
3+
record TemplateOperationResult(boolean success, String message) {
4+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package pl.commit.craft.template;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Getter;
5+
import java.util.Set;
6+
7+
@AllArgsConstructor
8+
class ValidationResult {
9+
@Getter
10+
private final boolean valid;
11+
private final Set<String> missingInPattern;
12+
private final Set<String> extraInPattern;
13+
14+
/**
15+
* Creates a successful validation result
16+
*/
17+
public static ValidationResult valid() {
18+
return new ValidationResult(true, Set.of(), Set.of());
19+
}
20+
21+
/**
22+
* Creates a validation result with errors
23+
*/
24+
public static ValidationResult invalid(Set<String> missingInPattern, Set<String> extraInPattern) {
25+
return new ValidationResult(false, missingInPattern, extraInPattern);
26+
}
27+
28+
/**
29+
* Generates a detailed error message for invalid templates
30+
*/
31+
public String getErrorMessage() {
32+
if (valid) {
33+
return "Template is valid";
34+
}
35+
36+
StringBuilder message = new StringBuilder("Invalid template format: ");
37+
38+
if (!missingInPattern.isEmpty()) {
39+
message.append("Keys missing in pattern: [")
40+
.append(String.join(", ", missingInPattern))
41+
.append("]");
42+
43+
if (!extraInPattern.isEmpty()) {
44+
message.append("; ");
45+
}
46+
}
47+
48+
if (!extraInPattern.isEmpty()) {
49+
message.append("Missing keys in model: [")
50+
.append(String.join(", ", extraInPattern))
51+
.append("]");
52+
}
53+
54+
return message.toString();
55+
}
56+
}

src/main/java/pl/commit/craft/template/generate/CommitTemplateGenerateController.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,10 @@ public ResponseEntity<String> generateCommit(@RequestParam String templateName,
1818
String commitMessage = service.generateCommit(templateName, commitData);
1919
return ResponseEntity.ok(commitMessage);
2020
}
21+
22+
@PostMapping("/generate-dedicated")
23+
public ResponseEntity<String> generateDedicatedCommit(@RequestParam String templateName, @RequestBody JsonNode commitData) throws IOException {
24+
String commitMessage = service.generateDedicatedCommit(templateName, commitData);
25+
return ResponseEntity.ok(commitMessage);
26+
}
2127
}

0 commit comments

Comments
 (0)