Skip to content

Commit 5ae838e

Browse files
committed
Implement Key Generation Strategy
1 parent e854772 commit 5ae838e

14 files changed

+308
-41
lines changed

spring-data-mock/pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727

2828
<groupId>com.mmnaseri.utils</groupId>
2929
<artifactId>spring-data-mock</artifactId>
30-
<version>2.2.0</version>
30+
<version>2.2.1</version>
3131

3232
<name>Spring Data Mock</name>
3333
<description>A framework for mocking Spring Data repositories</description>
@@ -327,4 +327,4 @@
327327
</profile>
328328
</profiles>
329329

330-
</project>
330+
</project>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.mmnaseri.utils.spring.data.domain;
2+
3+
public enum KeyGenerationStrategy {
4+
/**
5+
* Generate a key for {@code null} id values only.
6+
*/
7+
ONLY_NULL,
8+
9+
/**
10+
* Generate a key for all "unmanaged" entites.
11+
* Regardless of whether an ID value exists or not.
12+
*/
13+
ALL_UNMANAGED
14+
}

spring-data-mock/src/main/java/com/mmnaseri/utils/spring/data/domain/KeyGeneratorAware.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,6 @@
1010
public interface KeyGeneratorAware<S> {
1111

1212
void setKeyGenerator(KeyGenerator<? extends S> keyGenerator);
13+
void setKeyGenerationStrategy(KeyGenerationStrategy strategy);
14+
1315
}

spring-data-mock/src/main/java/com/mmnaseri/utils/spring/data/dsl/factory/RepositoryFactoryBuilder.java

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
package com.mmnaseri.utils.spring.data.dsl.factory;
22

3-
import com.mmnaseri.utils.spring.data.domain.KeyGenerator;
4-
import com.mmnaseri.utils.spring.data.domain.Operator;
5-
import com.mmnaseri.utils.spring.data.domain.OperatorContext;
6-
import com.mmnaseri.utils.spring.data.domain.RepositoryMetadataResolver;
3+
import com.mmnaseri.utils.spring.data.domain.*;
74
import com.mmnaseri.utils.spring.data.domain.impl.DefaultOperatorContext;
85
import com.mmnaseri.utils.spring.data.domain.impl.DefaultRepositoryMetadataResolver;
96
import com.mmnaseri.utils.spring.data.domain.impl.MethodQueryDescriptionExtractor;
@@ -64,6 +61,7 @@ public class RepositoryFactoryBuilder
6461
private DataStoreEventListenerContext eventListenerContext;
6562
private NonDataOperationInvocationHandler operationInvocationHandler;
6663
private KeyGenerator<?> defaultKeyGenerator;
64+
private KeyGenerationStrategy defaultKeyGenerationStrategy;
6765

6866
private RepositoryFactoryBuilder() {
6967
metadataResolver = new DefaultRepositoryMetadataResolver();
@@ -76,6 +74,7 @@ private RepositoryFactoryBuilder() {
7674
operationInvocationHandler = new NonDataOperationInvocationHandler();
7775
// by default, we do not want any key generator, unless one is specified
7876
defaultKeyGenerator = null;
77+
defaultKeyGenerationStrategy = KeyGenerationStrategy.ONLY_NULL;
7978
}
8079

8180
@Override
@@ -251,17 +250,28 @@ public RepositoryFactoryConfiguration configure() {
251250
typeMappingContext,
252251
eventListenerContext,
253252
operationInvocationHandler,
254-
defaultKeyGenerator);
253+
defaultKeyGenerator,
254+
defaultKeyGenerationStrategy);
255255
}
256256

257257
@Override
258258
public <S> Implementation generateKeysUsing(KeyGenerator<S> keyGenerator) {
259-
return new RepositoryMockBuilder().useFactory(build()).generateKeysUsing(keyGenerator);
259+
return generateKeysUsing(keyGenerator, defaultKeyGenerationStrategy);
260+
}
261+
262+
@Override
263+
public <S> Implementation generateKeysUsing(KeyGenerator<S> keyGenerator, KeyGenerationStrategy keyGenerationStrategy) {
264+
return new RepositoryMockBuilder().useFactory(build()).generateKeysUsing(keyGenerator, keyGenerationStrategy);
260265
}
261266

262267
@Override
263268
public <S, G extends KeyGenerator<S>> Implementation generateKeysUsing(Class<G> generatorType) {
264-
return new RepositoryMockBuilder().useFactory(build()).generateKeysUsing(generatorType);
269+
return generateKeysUsing(generatorType, defaultKeyGenerationStrategy);
270+
}
271+
272+
@Override
273+
public <S, G extends KeyGenerator<S>> Implementation generateKeysUsing(Class<G> generatorType, KeyGenerationStrategy keyGenerationStrategy) {
274+
return new RepositoryMockBuilder().useFactory(build()).generateKeysUsing(generatorType, keyGenerationStrategy);
265275
}
266276

267277
@Override
@@ -291,7 +301,8 @@ public static RepositoryFactoryConfiguration defaultConfiguration() {
291301
builder.typeMappingContext,
292302
builder.eventListenerContext,
293303
builder.operationInvocationHandler,
294-
builder.defaultKeyGenerator);
304+
builder.defaultKeyGenerator,
305+
builder.defaultKeyGenerationStrategy);
295306
}
296307

297308
/**

spring-data-mock/src/main/java/com/mmnaseri/utils/spring/data/dsl/mock/KeyGeneration.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.mmnaseri.utils.spring.data.dsl.mock;
22

3+
import com.mmnaseri.utils.spring.data.domain.KeyGenerationStrategy;
34
import com.mmnaseri.utils.spring.data.domain.KeyGenerator;
45

56
/**
@@ -19,6 +20,17 @@ public interface KeyGeneration extends Implementation {
1920
*/
2021
<S> Implementation generateKeysUsing(KeyGenerator<S> keyGenerator);
2122

23+
/**
24+
* Sets the key generator to the provided instance
25+
*
26+
* @param keyGenerator the key generator
27+
* @param keyGenerationStrategy the key generation strategy
28+
* @param <S> the type of the keys
29+
* @return the rest of the configuration
30+
*/
31+
<S> Implementation generateKeysUsing(
32+
KeyGenerator<S> keyGenerator, KeyGenerationStrategy keyGenerationStrategy);
33+
2234
/**
2335
* Sets the key generator to an instance of the provided type.
2436
*
@@ -29,6 +41,18 @@ public interface KeyGeneration extends Implementation {
2941
*/
3042
<S, G extends KeyGenerator<S>> Implementation generateKeysUsing(Class<G> generatorType);
3143

44+
/**
45+
* Sets the key generator to an instance of the provided type.
46+
*
47+
* @param generatorType the type of the key generator to use
48+
* @param keyGenerationStrategy the key generation strategy
49+
* @param <S> the type of the keys the generator will be generating
50+
* @param <G> the type of the generator
51+
* @return the rest of the configuration
52+
*/
53+
<S, G extends KeyGenerator<S>> Implementation generateKeysUsing(
54+
Class<G> generatorType, KeyGenerationStrategy keyGenerationStrategy);
55+
3256
/**
3357
* Tells the builder that we are not going to have any auto-generated keys
3458
*

spring-data-mock/src/main/java/com/mmnaseri/utils/spring/data/dsl/mock/RepositoryMockBuilder.java

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.mmnaseri.utils.spring.data.dsl.mock;
22

3+
import com.mmnaseri.utils.spring.data.domain.KeyGenerationStrategy;
34
import com.mmnaseri.utils.spring.data.domain.KeyGenerator;
45
import com.mmnaseri.utils.spring.data.domain.RepositoryMetadata;
56
import com.mmnaseri.utils.spring.data.domain.impl.key.NoOpKeyGenerator;
@@ -11,6 +12,7 @@
1112

1213
import java.util.LinkedList;
1314
import java.util.List;
15+
import java.util.Optional;
1416

1517
/**
1618
* This class implements the interfaces used to define a DSL for mocking a repository.
@@ -23,34 +25,36 @@ public class RepositoryMockBuilder implements Start, ImplementationAnd, KeyGener
2325
private final RepositoryFactory factory;
2426
private final List<Class<?>> implementations;
2527
private final KeyGenerator<?> keyGenerator;
28+
private final KeyGenerationStrategy keyGenerationStrategy;
2629

2730
public RepositoryMockBuilder() {
28-
this(null, new LinkedList<>(), null);
31+
this(null, new LinkedList<>(), null, null);
2932
}
3033

3134
private RepositoryMockBuilder(
32-
RepositoryFactory factory, List<Class<?>> implementations, KeyGenerator<?> keyGenerator) {
35+
RepositoryFactory factory, List<Class<?>> implementations, KeyGenerator<?> keyGenerator, KeyGenerationStrategy keyGenerationStrategy) {
3336
this.factory = factory;
3437
this.implementations = implementations;
3538
this.keyGenerator = keyGenerator;
39+
this.keyGenerationStrategy = keyGenerationStrategy;
3640
}
3741

3842
@Override
3943
public KeyGeneration useConfiguration(RepositoryFactoryConfiguration configuration) {
4044
return new RepositoryMockBuilder(
41-
new DefaultRepositoryFactory(configuration), implementations, keyGenerator);
45+
new DefaultRepositoryFactory(configuration), implementations, keyGenerator, keyGenerationStrategy);
4246
}
4347

4448
@Override
4549
public KeyGeneration useFactory(RepositoryFactory factory) {
46-
return new RepositoryMockBuilder(factory, implementations, keyGenerator);
50+
return new RepositoryMockBuilder(factory, implementations, keyGenerator, keyGenerationStrategy);
4751
}
4852

4953
@Override
5054
public ImplementationAnd usingImplementation(Class<?> implementation) {
5155
final LinkedList<Class<?>> implementations = new LinkedList<>(this.implementations);
5256
implementations.add(implementation);
53-
return new RepositoryMockBuilder(factory, implementations, keyGenerator);
57+
return new RepositoryMockBuilder(factory, implementations, keyGenerator, keyGenerationStrategy);
5458
}
5559

5660
@Override
@@ -60,19 +64,29 @@ public ImplementationAnd and(Class<?> implementation) {
6064

6165
@Override
6266
public <S> Implementation generateKeysUsing(KeyGenerator<S> keyGenerator) {
63-
return new RepositoryMockBuilder(factory, implementations, keyGenerator);
67+
return generateKeysUsing(keyGenerator, keyGenerationStrategy);
68+
}
69+
70+
@Override
71+
public <S> Implementation generateKeysUsing(KeyGenerator<S> keyGenerator, KeyGenerationStrategy keyGenerationStrategy) {
72+
return new RepositoryMockBuilder(factory, implementations, keyGenerator, keyGenerationStrategy);
6473
}
6574

6675
@Override
6776
public <S, G extends KeyGenerator<S>> Implementation generateKeysUsing(Class<G> generatorType) {
77+
return generateKeysUsing(generatorType, keyGenerationStrategy);
78+
}
79+
80+
@Override
81+
public <S, G extends KeyGenerator<S>> Implementation generateKeysUsing(Class<G> generatorType, KeyGenerationStrategy keyGenerationStrategy) {
6882
//noinspection unchecked
6983
final G instance = (G) createKeyGenerator(generatorType);
70-
return generateKeysUsing(instance);
84+
return generateKeysUsing(instance, keyGenerationStrategy);
7185
}
7286

7387
@Override
7488
public Implementation withoutGeneratingKeys() {
75-
return new RepositoryMockBuilder(factory, implementations, new NoOpKeyGenerator<>());
89+
return new RepositoryMockBuilder(factory, implementations, new NoOpKeyGenerator<>(), keyGenerationStrategy);
7690
}
7791

7892
private KeyGenerator<?> createKeyGenerator(Class<? extends KeyGenerator> generatorType) {
@@ -108,10 +122,10 @@ public <E> E mock(Class<E> repositoryInterface) {
108122
generatorProvider.getKeyGenerator(identifierType);
109123
evaluatedKeyGenerator = createKeyGenerator(keyGeneratorType);
110124
}
111-
return generateKeysUsing(evaluatedKeyGenerator).mock(repositoryInterface);
125+
return generateKeysUsing(evaluatedKeyGenerator, keyGenerationStrategy).mock(repositoryInterface);
112126
} else {
113127
return repositoryFactory.getInstance(
114-
keyGenerator, repositoryInterface, implementations.toArray(new Class[0]));
128+
keyGenerator, keyGenerationStrategy, repositoryInterface, implementations.toArray(new Class[0]));
115129
}
116130
}
117131
}

spring-data-mock/src/main/java/com/mmnaseri/utils/spring/data/proxy/RepositoryFactory.java

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.mmnaseri.utils.spring.data.proxy;
22

3+
import com.mmnaseri.utils.spring.data.domain.KeyGenerationStrategy;
34
import com.mmnaseri.utils.spring.data.domain.KeyGenerator;
45

56
/**
@@ -26,9 +27,30 @@ public interface RepositoryFactory {
2627
* @param <E> the type of the interface
2728
* @return a prepared instance of the repository
2829
* @throws com.mmnaseri.utils.spring.data.error.RepositoryMockException should anything go wrong
30+
* @deprecated Use {@link #getInstance(KeyGenerator, KeyGenerationStrategy, Class, Class[])} instead.
31+
*/
32+
@Deprecated
33+
default <E> E getInstance(
34+
KeyGenerator<?> keyGenerator, Class<E> repositoryInterface, Class... implementations) {
35+
return getInstance(keyGenerator, KeyGenerationStrategy.ONLY_NULL, repositoryInterface, implementations);
36+
}
37+
38+
/**
39+
* Creates an instance of the repository as per the provided configuration.
40+
*
41+
* @param keyGenerator the key generator to use when inserting items (if auto generation is
42+
* required). You can specify a {@literal null} key generator to signify that {@link
43+
* RepositoryFactoryConfiguration#getDefaultKeyGenerator() the fallback key generator} should
44+
* be used when generating keys.
45+
* @param keyGenerationStrategy the strategy when and for which entities a key should be generated.
46+
* @param repositoryInterface the repository interface which we want to mock
47+
* @param implementations all the concrete classes that can be used to figure out method mappings
48+
* @param <E> the type of the interface
49+
* @return a prepared instance of the repository
50+
* @throws com.mmnaseri.utils.spring.data.error.RepositoryMockException should anything go wrong
2951
*/
3052
<E> E getInstance(
31-
KeyGenerator<?> keyGenerator, Class<E> repositoryInterface, Class... implementations);
53+
KeyGenerator<?> keyGenerator, KeyGenerationStrategy keyGenerationStrategy, Class<E> repositoryInterface, Class... implementations);
3254

3355
/** @return the configuration bound to this repository factory */
3456
RepositoryFactoryConfiguration getConfiguration();

spring-data-mock/src/main/java/com/mmnaseri/utils/spring/data/proxy/RepositoryFactoryConfiguration.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.mmnaseri.utils.spring.data.proxy;
22

3+
import com.mmnaseri.utils.spring.data.domain.KeyGenerationStrategy;
34
import com.mmnaseri.utils.spring.data.domain.KeyGenerator;
45
import com.mmnaseri.utils.spring.data.domain.RepositoryMetadataResolver;
56
import com.mmnaseri.utils.spring.data.domain.impl.MethodQueryDescriptionExtractor;
@@ -46,4 +47,10 @@ public interface RepositoryFactoryConfiguration {
4647
* specified
4748
*/
4849
KeyGenerator<?> getDefaultKeyGenerator();
50+
51+
/**
52+
* @return the default key generation strategy that should be used as a fallback when no strategy is
53+
* specified
54+
*/
55+
KeyGenerationStrategy getDefaultKeyGenerationStrategy();
4956
}

spring-data-mock/src/main/java/com/mmnaseri/utils/spring/data/proxy/impl/DefaultRepositoryFactory.java

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,6 @@
11
package com.mmnaseri.utils.spring.data.proxy.impl;
22

3-
import com.mmnaseri.utils.spring.data.domain.DataStoreAware;
4-
import com.mmnaseri.utils.spring.data.domain.KeyGenerator;
5-
import com.mmnaseri.utils.spring.data.domain.KeyGeneratorAware;
6-
import com.mmnaseri.utils.spring.data.domain.RepositoryAware;
7-
import com.mmnaseri.utils.spring.data.domain.RepositoryMetadata;
8-
import com.mmnaseri.utils.spring.data.domain.RepositoryMetadataAware;
9-
import com.mmnaseri.utils.spring.data.domain.RepositoryMetadataResolver;
3+
import com.mmnaseri.utils.spring.data.domain.*;
104
import com.mmnaseri.utils.spring.data.domain.impl.MethodQueryDescriptionExtractor;
115
import com.mmnaseri.utils.spring.data.domain.impl.key.NoOpKeyGenerator;
126
import com.mmnaseri.utils.spring.data.proxy.DataOperationResolver;
@@ -73,7 +67,7 @@ public DefaultRepositoryFactory(RepositoryFactoryConfiguration configuration) {
7367

7468
@Override
7569
public <E> E getInstance(
76-
KeyGenerator<?> keyGenerator, Class<E> repositoryInterface, Class... implementations) {
70+
KeyGenerator<?> keyGenerator, KeyGenerationStrategy keyGenerationStrategy, Class<E> repositoryInterface, Class... implementations) {
7771
final KeyGenerator<?> actualKeyGenerator;
7872
if (keyGenerator == null) {
7973
if (configuration.getDefaultKeyGenerator() != null) {
@@ -87,6 +81,19 @@ public <E> E getInstance(
8781
} else {
8882
actualKeyGenerator = keyGenerator;
8983
}
84+
final KeyGenerationStrategy actualKeyGenerationStrategy;
85+
if (keyGenerationStrategy == null) {
86+
if (configuration.getDefaultKeyGenerator() != null) {
87+
// if no key generation strategy is passed and there is a default strategy specified, we
88+
// fall back to that
89+
actualKeyGenerationStrategy = configuration.getDefaultKeyGenerationStrategy();
90+
} else {
91+
// otherwise, fall back to ONLY_NULL
92+
actualKeyGenerationStrategy = KeyGenerationStrategy.ONLY_NULL;
93+
}
94+
} else {
95+
actualKeyGenerationStrategy = keyGenerationStrategy;
96+
}
9097
log.info(
9198
"We are going to create a proxy instance of type "
9299
+ repositoryInterface
@@ -104,7 +111,7 @@ public <E> E getInstance(
104111
log.info(
105112
"Trying to find all the proper type mappings for entity repository " + repositoryInterface);
106113
final List<TypeMapping<?>> typeMappings =
107-
getTypeMappings(metadata, dataStore, actualKeyGenerator, implementations);
114+
getTypeMappings(metadata, dataStore, actualKeyGenerator, actualKeyGenerationStrategy, implementations);
108115
// set up the data operation resolver
109116
final DataOperationResolver operationResolver =
110117
new DefaultDataOperationResolver(
@@ -176,13 +183,15 @@ public RepositoryFactoryConfiguration getConfiguration() {
176183
* @param metadata the repository metadata
177184
* @param dataStore the data store
178185
* @param keyGenerator the key generator
186+
* @param keyGenerationStrategy
179187
* @param implementations the implementations specified by the user
180188
* @return the resolved list of type mappings
181189
*/
182190
private List<TypeMapping<?>> getTypeMappings(
183191
RepositoryMetadata metadata,
184192
DataStore<Object, ?> dataStore,
185193
KeyGenerator<?> keyGenerator,
194+
KeyGenerationStrategy keyGenerationStrategy,
186195
Class[] implementations) {
187196
final TypeMappingContext localContext = new DefaultTypeMappingContext(typeMappingContext);
188197
for (Class implementation : implementations) {
@@ -203,6 +212,7 @@ private List<TypeMapping<?>> getTypeMappings(
203212
KeyGeneratorAware instance = (KeyGeneratorAware) mapping.getInstance();
204213
//noinspection unchecked
205214
instance.setKeyGenerator(keyGenerator);
215+
instance.setKeyGenerationStrategy(keyGenerationStrategy);
206216
}
207217
if (mapping.getInstance() instanceof RepositoryFactoryConfigurationAware) {
208218
RepositoryFactoryConfigurationAware instance =

0 commit comments

Comments
 (0)