Skip to content

Commit ece9a3d

Browse files
refactor: improvements and issues (#22)
1 parent 8643b28 commit ece9a3d

18 files changed

Lines changed: 790 additions & 380 deletions

ANALYSIS.md

Lines changed: 471 additions & 321 deletions
Large diffs are not rendered by default.

src/main/java/io/flamingock/template/mongodb/mapper/IndexOptionsMapper.java

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,15 @@
1414
* limitations under the License.
1515
*/
1616
package io.flamingock.template.mongodb.mapper;
17-
import com.mongodb.client.model.IndexOptions;
1817

18+
import com.mongodb.client.model.IndexOptions;
1919

2020
import java.util.Arrays;
2121
import java.util.Collections;
2222
import java.util.HashSet;
2323
import java.util.Map;
2424
import java.util.Set;
25+
import java.util.concurrent.TimeUnit;
2526

2627
import static io.flamingock.template.mongodb.mapper.MapperUtil.getBoolean;
2728
import static io.flamingock.template.mongodb.mapper.MapperUtil.getBson;
@@ -31,17 +32,19 @@
3132
import static io.flamingock.template.mongodb.mapper.MapperUtil.getLong;
3233
import static io.flamingock.template.mongodb.mapper.MapperUtil.getString;
3334

34-
import java.util.concurrent.TimeUnit;
35-
3635

3736
public final class IndexOptionsMapper {
3837

3938
public static final Set<String> RECOGNIZED_KEYS = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
4039
"background", "unique", "name", "sparse", "expireAfterSeconds",
4140
"version", "weights", "defaultLanguage", "languageOverride",
4241
"textVersion", "sphereVersion", "bits", "min", "max",
43-
"bucketSize", "storageEngine", "partialFilterExpression",
44-
"collation", "wildcardProjection", "hidden"
42+
"storageEngine", "partialFilterExpression", "collation"
43+
)));
44+
45+
/** Options removed from the MongoDB Java driver: rejected at validation time with a clear error. */
46+
public static final Set<String> UNSUPPORTED_KEYS = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
47+
"bucketSize", "wildcardProjection", "hidden"
4548
)));
4649

4750
private IndexOptionsMapper() {}
@@ -91,9 +94,6 @@ public static IndexOptions mapToIndexOptions(Map<String, Object> options) {
9194
if (options.containsKey("max")) {
9295
indexOptions.max(getDouble(options, "max"));
9396
}
94-
if (options.containsKey("bucketSize")) {
95-
throw new UnsupportedOperationException("bulkSize option is not supported in MongoDB driver versions 4.4.0 and above");
96-
}
9797
if (options.containsKey("storageEngine")) {
9898
indexOptions.storageEngine(getBson(options, "storageEngine"));
9999
}
@@ -103,12 +103,6 @@ public static IndexOptions mapToIndexOptions(Map<String, Object> options) {
103103
if (options.containsKey("collation")) {
104104
indexOptions.collation(getCollation(options, "collation"));
105105
}
106-
if (options.containsKey("wildcardProjection")) {
107-
throw new UnsupportedOperationException("wildcardProjection option is not supported in MongoDB driver versions 4.1.0 and above");
108-
}
109-
if (options.containsKey("hidden")) {
110-
throw new UnsupportedOperationException("hidden option is not supported in MongoDB driver versions 4.1.0 and above");
111-
}
112106

113107
return indexOptions;
114108
}

src/main/java/io/flamingock/template/mongodb/mapper/MapperUtil.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -105,28 +105,28 @@ private static Collation buildCollationFromMap(Map<String, Object> map) {
105105
Collation.Builder builder = Collation.builder().locale((String) locale);
106106

107107
if (map.containsKey("caseLevel")) {
108-
builder.caseLevel((Boolean) map.get("caseLevel"));
108+
builder.caseLevel(getBoolean(map, "caseLevel"));
109109
}
110110
if (map.containsKey("caseFirst")) {
111-
builder.collationCaseFirst(CollationCaseFirst.fromString((String) map.get("caseFirst")));
111+
builder.collationCaseFirst(CollationCaseFirst.fromString(getString(map, "caseFirst")));
112112
}
113113
if (map.containsKey("strength")) {
114-
builder.collationStrength(CollationStrength.fromInt(((Number) map.get("strength")).intValue()));
114+
builder.collationStrength(CollationStrength.fromInt(getInteger(map, "strength")));
115115
}
116116
if (map.containsKey("numericOrdering")) {
117-
builder.numericOrdering((Boolean) map.get("numericOrdering"));
117+
builder.numericOrdering(getBoolean(map, "numericOrdering"));
118118
}
119119
if (map.containsKey("alternate")) {
120-
builder.collationAlternate(CollationAlternate.fromString((String) map.get("alternate")));
120+
builder.collationAlternate(CollationAlternate.fromString(getString(map, "alternate")));
121121
}
122122
if (map.containsKey("maxVariable")) {
123-
builder.collationMaxVariable(CollationMaxVariable.fromString((String) map.get("maxVariable")));
123+
builder.collationMaxVariable(CollationMaxVariable.fromString(getString(map, "maxVariable")));
124124
}
125125
if (map.containsKey("normalization")) {
126-
builder.normalization((Boolean) map.get("normalization"));
126+
builder.normalization(getBoolean(map, "normalization"));
127127
}
128128
if (map.containsKey("backwards")) {
129-
builder.backwards((Boolean) map.get("backwards"));
129+
builder.backwards(getBoolean(map, "backwards"));
130130
}
131131
return builder.build();
132132
}

src/main/java/io/flamingock/template/mongodb/model/MongoOperation.java

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,23 +52,43 @@ public class MongoOperation implements TemplatePayload {
5252

5353
@SuppressWarnings("unchecked")
5454
public Document getKeys() {
55-
return new Document((Map<String, Object>) parameters.get("keys"));
55+
Object value = parameters.get("keys");
56+
if (!(value instanceof Map)) {
57+
throw new IllegalStateException(
58+
"getKeys() called but 'keys' is not a Map — validate() must run before operator execution");
59+
}
60+
return new Document((Map<String, Object>) value);
5661
}
5762

5863
@SuppressWarnings("unchecked")
5964
public Document getOptions() {
60-
return parameters.containsKey("options")
61-
? new Document((Map<String, Object>) parameters.get("options"))
62-
: new Document();
65+
Object value = parameters.get("options");
66+
if (value == null) {
67+
return new Document();
68+
}
69+
if (!(value instanceof Map)) {
70+
throw new IllegalStateException(
71+
"getOptions() called but 'options' is not a Map — validate() must run before operator execution");
72+
}
73+
return new Document((Map<String, Object>) value);
6374
}
6475

6576
@SuppressWarnings("unchecked")
6677
public Document getFilter() {
67-
return new Document((Map<String, Object>) parameters.get("filter"));
78+
Object value = parameters.get("filter");
79+
if (!(value instanceof Map)) {
80+
throw new IllegalStateException(
81+
"getFilter() called but 'filter' is not a Map — validate() must run before operator execution");
82+
}
83+
return new Document((Map<String, Object>) value);
6884
}
6985

7086
public boolean isMulti() {
7187
Object multi = parameters.get("multi");
88+
if (multi != null && !(multi instanceof Boolean)) {
89+
throw new IllegalStateException(
90+
"isMulti() called but 'multi' is not a Boolean — validate() must run before operator execution");
91+
}
7292
return multi != null && (Boolean) multi;
7393
}
7494

src/main/java/io/flamingock/template/mongodb/model/MongoOperationType.java

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,9 @@
3939
import io.flamingock.template.mongodb.validation.RenameCollectionParametersValidator;
4040
import io.flamingock.template.mongodb.validation.UpdateParametersValidator;
4141

42-
import java.util.Arrays;
42+
import java.util.Collections;
43+
import java.util.HashMap;
44+
import java.util.Map;
4345
import java.util.Optional;
4446
import java.util.function.BiFunction;
4547

@@ -57,6 +59,15 @@ public enum MongoOperationType {
5759
CREATE_VIEW("createView", CreateViewOperator::new, new CreateViewParametersValidator(), false),
5860
DROP_VIEW("dropView", DropViewOperator::new, new NoParametersValidator("DropView"), false);
5961

62+
private static final Map<String, MongoOperationType> LOOKUP;
63+
static {
64+
Map<String, MongoOperationType> map = new HashMap<>();
65+
for (MongoOperationType t : values()) {
66+
map.put(t.value, t);
67+
}
68+
LOOKUP = Collections.unmodifiableMap(map);
69+
}
70+
6071
private final String value;
6172
private final BiFunction<MongoDatabase, MongoOperation, MongoOperator> createOperatorFunction;
6273
private final OperationValidator operationValidator;
@@ -73,16 +84,15 @@ public enum MongoOperationType {
7384
}
7485

7586
public static MongoOperationType findByTypeOrThrow(String typeValue) {
76-
return Arrays.stream(MongoOperationType.values())
77-
.filter(type -> type.matches(typeValue))
78-
.findFirst()
79-
.orElseThrow(() -> new IllegalArgumentException("MongoOperation not supported: " + typeValue));
87+
MongoOperationType found = LOOKUP.get(typeValue);
88+
if (found == null) {
89+
throw new IllegalArgumentException("MongoOperation not supported: " + typeValue);
90+
}
91+
return found;
8092
}
8193

8294
public static Optional<MongoOperationType> findByType(String typeValue) {
83-
return Arrays.stream(MongoOperationType.values())
84-
.filter(type -> type.matches(typeValue))
85-
.findFirst();
95+
return Optional.ofNullable(LOOKUP.get(typeValue));
8696
}
8797

8898
public MongoOperator getOperator(MongoDatabase mongoDatabase, MongoOperation operation) {
@@ -96,8 +106,4 @@ public OperationValidator getOperationValidator() {
96106
public boolean isTransactional() {
97107
return transactional;
98108
}
99-
100-
private boolean matches(String operationType) {
101-
return this.value.equals(operationType);
102-
}
103109
}

src/main/java/io/flamingock/template/mongodb/model/operator/CreateViewOperator.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,12 @@ private String getViewOn() {
4848

4949
@SuppressWarnings("unchecked")
5050
private List<Document> getPipeline() {
51-
List<Map<String, Object>> rawPipeline = (List<Map<String, Object>>) op.getParameters().get("pipeline");
52-
return rawPipeline != null
53-
? rawPipeline.stream().map(Document::new).collect(Collectors.toList())
54-
: null;
51+
Object value = op.getParameters().get("pipeline");
52+
if (!(value instanceof List)) {
53+
throw new IllegalStateException(
54+
"getPipeline() called but 'pipeline' is not a List — validate() must run before operator execution");
55+
}
56+
List<Map<String, Object>> rawPipeline = (List<Map<String, Object>>) value;
57+
return rawPipeline.stream().map(Document::new).collect(Collectors.toList());
5558
}
5659
}

src/main/java/io/flamingock/template/mongodb/model/operator/RenameCollectionOperator.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@ protected void applyInternal(ClientSession clientSession) {
3939
return;
4040
}
4141

42+
if (!sourceExists) {
43+
logger.warn("Source collection '{}' does not exist and target '{}' is also absent — skipping renameCollection",
44+
op.getCollection(), targetName);
45+
return;
46+
}
47+
4248
MongoNamespace target = new MongoNamespace(mongoDatabase.getName(), targetName);
4349
RenameCollectionOptions options = RenameCollectionOptionsMapper.map(op.getOptions());
4450
mongoDatabase.getCollection(op.getCollection()).renameCollection(target, options);

src/main/java/io/flamingock/template/mongodb/validation/CollectionValidator.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ public List<TemplatePayloadValidationError> validate(MongoOperation operation) {
3939
} else if (collection.contains("\0")) {
4040
return Collections.singletonList(
4141
new TemplatePayloadValidationError("collection", "Collection name cannot contain null character"));
42+
} else if (collection.startsWith("system.")) {
43+
return Collections.singletonList(
44+
new TemplatePayloadValidationError("collection", "Collection name cannot start with 'system.': " + collection));
4245
}
4346

4447
return Collections.emptyList();

src/main/java/io/flamingock/template/mongodb/validation/CreateIndexParametersValidator.java

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,30 @@
2121

2222
import java.util.ArrayList;
2323
import java.util.Arrays;
24+
import java.util.Collections;
2425
import java.util.HashSet;
2526
import java.util.List;
2627
import java.util.Map;
2728
import java.util.Set;
2829

30+
import static java.lang.String.format;
31+
2932
public class CreateIndexParametersValidator implements OperationValidator {
3033

3134
private static final Set<String> RECOGNIZED_KEYS = new HashSet<>(Arrays.asList("keys", "options"));
3235

36+
/**
37+
* Union of RECOGNIZED_KEYS and UNSUPPORTED_KEYS from the mapper. Used by the unrecognized-key
38+
* check so that unsupported keys only generate their specific "not supported" error and do not
39+
* also trigger the generic "unrecognized option" error.
40+
*/
41+
private static final Set<String> ALL_KNOWN_OPTION_KEYS;
42+
static {
43+
Set<String> combined = new HashSet<>(IndexOptionsMapper.RECOGNIZED_KEYS);
44+
combined.addAll(IndexOptionsMapper.UNSUPPORTED_KEYS);
45+
ALL_KNOWN_OPTION_KEYS = Collections.unmodifiableSet(combined);
46+
}
47+
3348
@Override
3449
public List<TemplatePayloadValidationError> validate(MongoOperation operation) {
3550
List<TemplatePayloadValidationError> errors = new ArrayList<>();
@@ -66,8 +81,16 @@ public List<TemplatePayloadValidationError> validate(MongoOperation operation) {
6681
} else if (options instanceof Map) {
6782
@SuppressWarnings("unchecked")
6883
Map<String, Object> optionsMap = (Map<String, Object>) options;
84+
for (String unsupported : IndexOptionsMapper.UNSUPPORTED_KEYS) {
85+
if (optionsMap.containsKey(unsupported)) {
86+
errors.add(new TemplatePayloadValidationError(
87+
"parameters.options." + unsupported,
88+
format("'%s' is not supported — removed from the MongoDB Java driver 4.x+", unsupported)));
89+
}
90+
}
91+
errors.addAll(OperationValidator.checkNullOptionValues(optionsMap, "CreateIndex"));
6992
errors.addAll(OperationValidator.checkUnrecognizedOptionKeys(
70-
optionsMap, IndexOptionsMapper.RECOGNIZED_KEYS, "CreateIndex"));
93+
optionsMap, ALL_KNOWN_OPTION_KEYS, "CreateIndex"));
7194
}
7295

7396
errors.addAll(OperationValidator.checkUnrecognizedKeys(params, RECOGNIZED_KEYS, "CreateIndex"));

src/main/java/io/flamingock/template/mongodb/validation/CreateViewParametersValidator.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ public List<TemplatePayloadValidationError> validate(MongoOperation operation) {
8383
} else if (options instanceof Map) {
8484
@SuppressWarnings("unchecked")
8585
Map<String, Object> optionsMap = (Map<String, Object>) options;
86+
errors.addAll(OperationValidator.checkNullOptionValues(optionsMap, "CreateView"));
8687
errors.addAll(OperationValidator.checkUnrecognizedOptionKeys(
8788
optionsMap, CreateViewOptionsMapper.RECOGNIZED_KEYS, "CreateView"));
8889
}

0 commit comments

Comments
 (0)