Skip to content

Commit 976d979

Browse files
authored
feat: add support to customize gcloud command of LocalDatastoreHelper (#137)
Refactor creation of LocalDatastoreHelper to provide a builder (in addition to the existing factory methods) which allow finer grained configuration including the ability to set a storage directory to be passed when starting the emulator.
1 parent d572efb commit 976d979

File tree

2 files changed

+176
-22
lines changed

2 files changed

+176
-22
lines changed

google-cloud-datastore/src/main/java/com/google/cloud/datastore/testing/LocalDatastoreHelper.java

+83-22
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@
3535
import java.util.List;
3636
import java.util.UUID;
3737
import java.util.concurrent.TimeoutException;
38-
import java.util.logging.Level;
3938
import java.util.logging.Logger;
4039
import org.threeten.bp.Duration;
4140

@@ -50,6 +49,7 @@ public class LocalDatastoreHelper extends BaseEmulatorHelper<DatastoreOptions> {
5049
private final List<EmulatorRunner> emulatorRunners;
5150
private final double consistency;
5251
private final Path gcdPath;
52+
private boolean storeOnDisk;
5353

5454
// Gcloud emulator settings
5555
private static final String GCLOUD_CMD_TEXT = "gcloud beta emulators datastore start";
@@ -78,39 +78,87 @@ public class LocalDatastoreHelper extends BaseEmulatorHelper<DatastoreOptions> {
7878
}
7979
}
8080

81-
private LocalDatastoreHelper(double consistency, int port) {
81+
/** A builder for {@code LocalDatastoreHelper} objects. */
82+
public static class Builder {
83+
private double consistency;
84+
private int port;
85+
private Path dataDir;
86+
private boolean storeOnDisk = true;
87+
88+
private Builder() {}
89+
90+
private Builder(LocalDatastoreHelper helper) {
91+
this.consistency = helper.consistency;
92+
this.dataDir = helper.gcdPath;
93+
this.storeOnDisk = helper.storeOnDisk;
94+
}
95+
96+
public Builder setConsistency(double consistency) {
97+
this.consistency = consistency;
98+
return this;
99+
}
100+
101+
public Builder setPort(int port) {
102+
this.port = port;
103+
return this;
104+
}
105+
106+
public Builder setDataDir(Path dataDir) {
107+
this.dataDir = dataDir;
108+
return this;
109+
}
110+
111+
public Builder setStoreOnDisk(boolean storeOnDisk) {
112+
this.storeOnDisk = storeOnDisk;
113+
return this;
114+
}
115+
116+
/** Creates a {@code LocalDatastoreHelper} object. */
117+
public LocalDatastoreHelper build() {
118+
return new LocalDatastoreHelper(this);
119+
}
120+
}
121+
122+
private LocalDatastoreHelper(Builder builder) {
82123
super(
83124
"datastore",
84-
port > 0 ? port : BaseEmulatorHelper.findAvailablePort(DEFAULT_PORT),
125+
builder.port > 0 ? builder.port : BaseEmulatorHelper.findAvailablePort(DEFAULT_PORT),
85126
PROJECT_ID_PREFIX + UUID.randomUUID().toString());
86-
Path tmpDirectory = null;
87-
try {
88-
tmpDirectory = Files.createTempDirectory("gcd");
89-
} catch (IOException ex) {
90-
getLogger().log(Level.WARNING, "Failed to create temporary directory");
91-
}
92-
this.gcdPath = tmpDirectory;
93-
this.consistency = consistency;
127+
this.consistency = builder.consistency > 0 ? builder.consistency : DEFAULT_CONSISTENCY;
128+
this.gcdPath = builder.dataDir;
129+
this.storeOnDisk = builder.storeOnDisk;
94130
String binName = BIN_NAME;
95131
if (isWindows()) {
96132
binName = BIN_NAME.replace("/", "\\");
97133
}
98134
List<String> gcloudCommand = new ArrayList<>(Arrays.asList(GCLOUD_CMD_TEXT.split(" ")));
99135
gcloudCommand.add(GCLOUD_CMD_PORT_FLAG + "localhost:" + getPort());
100-
gcloudCommand.add(CONSISTENCY_FLAG + consistency);
101-
gcloudCommand.add("--no-store-on-disk");
136+
gcloudCommand.add(CONSISTENCY_FLAG + builder.consistency);
137+
if (!builder.storeOnDisk) {
138+
gcloudCommand.add("--no-store-on-disk");
139+
}
102140
GcloudEmulatorRunner gcloudRunner =
103141
new GcloudEmulatorRunner(gcloudCommand, VERSION_PREFIX, MIN_VERSION);
104142
List<String> binCommand = new ArrayList<>(Arrays.asList(binName, "start"));
105143
binCommand.add("--testing");
106144
binCommand.add(BIN_CMD_PORT_FLAG + getPort());
107-
binCommand.add(CONSISTENCY_FLAG + consistency);
108-
if (gcdPath != null) {
109-
gcloudCommand.add("--data-dir=" + gcdPath.toString());
145+
binCommand.add(CONSISTENCY_FLAG + getConsistency());
146+
if (builder.dataDir != null) {
147+
gcloudCommand.add("--data-dir=" + getGcdPath());
110148
}
111149
DownloadableEmulatorRunner downloadRunner =
112150
new DownloadableEmulatorRunner(binCommand, EMULATOR_URL, MD5_CHECKSUM);
113-
emulatorRunners = ImmutableList.of(gcloudRunner, downloadRunner);
151+
this.emulatorRunners = ImmutableList.of(gcloudRunner, downloadRunner);
152+
}
153+
154+
/** Returns a builder for {@code LocalDatastoreHelper} object. */
155+
public LocalDatastoreHelper.Builder toBuilder() {
156+
return new Builder(this);
157+
}
158+
159+
/** Returns a builder for {@code LocalDatastoreHelper} object. */
160+
public static LocalDatastoreHelper.Builder newBuilder() {
161+
return new LocalDatastoreHelper.Builder();
114162
}
115163

116164
@Override
@@ -153,6 +201,16 @@ public double getConsistency() {
153201
return consistency;
154202
}
155203

204+
/** Returns the data directory path of the local Datastore emulator. */
205+
public Path getGcdPath() {
206+
return gcdPath;
207+
}
208+
209+
/** Returns {@code true} data persist on disk, otherwise {@code false} data not store on disk. */
210+
public boolean isStoreOnDisk() {
211+
return storeOnDisk;
212+
}
213+
156214
/**
157215
* Creates a local Datastore helper with the specified settings for project ID and consistency.
158216
*
@@ -162,7 +220,7 @@ public double getConsistency() {
162220
* consistency of non-ancestor queries; non-ancestor queries are eventually consistent.
163221
*/
164222
public static LocalDatastoreHelper create(double consistency) {
165-
return create(consistency, 0);
223+
return LocalDatastoreHelper.newBuilder().setConsistency(consistency).setPort(0).build();
166224
}
167225

168226
/**
@@ -176,7 +234,7 @@ public static LocalDatastoreHelper create(double consistency) {
176234
* emulator will search for a free random port.
177235
*/
178236
public static LocalDatastoreHelper create(double consistency, int port) {
179-
return new LocalDatastoreHelper(consistency, port);
237+
return LocalDatastoreHelper.newBuilder().setConsistency(consistency).setPort(port).build();
180238
}
181239

182240
/**
@@ -187,7 +245,10 @@ public static LocalDatastoreHelper create(double consistency, int port) {
187245
* emulator will search for a free random port.
188246
*/
189247
public static LocalDatastoreHelper create(int port) {
190-
return new LocalDatastoreHelper(DEFAULT_CONSISTENCY, port);
248+
return LocalDatastoreHelper.newBuilder()
249+
.setConsistency(DEFAULT_CONSISTENCY)
250+
.setPort(port)
251+
.build();
191252
}
192253

193254
/**
@@ -197,7 +258,7 @@ public static LocalDatastoreHelper create(int port) {
197258
* all writes are immediately visible.
198259
*/
199260
public static LocalDatastoreHelper create() {
200-
return create(DEFAULT_CONSISTENCY);
261+
return LocalDatastoreHelper.newBuilder().setConsistency(DEFAULT_CONSISTENCY).build();
201262
}
202263

203264
/**
@@ -254,7 +315,7 @@ public void stop() throws IOException, InterruptedException, TimeoutException {
254315
stop(Duration.ofSeconds(20));
255316
}
256317

257-
private static void deleteRecursively(Path path) throws IOException {
318+
static void deleteRecursively(Path path) throws IOException {
258319
if (path == null || !Files.exists(path)) {
259320
return;
260321
}

google-cloud-datastore/src/test/java/com/google/cloud/datastore/testing/ITLocalDatastoreHelperTest.java

+93
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,12 @@
3030
import com.google.cloud.datastore.Entity;
3131
import com.google.cloud.datastore.Key;
3232
import java.io.IOException;
33+
import java.nio.file.Files;
34+
import java.nio.file.Path;
3335
import java.util.concurrent.TimeoutException;
36+
import org.junit.After;
3437
import org.junit.Assert;
38+
import org.junit.Before;
3539
import org.junit.Test;
3640
import org.junit.runner.RunWith;
3741
import org.junit.runners.JUnit4;
@@ -43,6 +47,17 @@ public class ITLocalDatastoreHelperTest {
4347
private static final double TOLERANCE = 0.00001;
4448
private static final String PROJECT_ID_PREFIX = "test-project-";
4549
private static final String NAMESPACE = "namespace";
50+
private Path dataDir;
51+
52+
@Before
53+
public void setUp() throws IOException {
54+
dataDir = Files.createTempDirectory("gcd");
55+
}
56+
57+
@After
58+
public void tearDown() throws IOException {
59+
LocalDatastoreHelper.deleteRecursively(dataDir);
60+
}
4661

4762
@Test
4863
public void testCreate() {
@@ -54,6 +69,57 @@ public void testCreate() {
5469
assertTrue(helper.getProjectId().startsWith(PROJECT_ID_PREFIX));
5570
}
5671

72+
@Test
73+
public void testCreateWithBuilder() {
74+
LocalDatastoreHelper helper =
75+
LocalDatastoreHelper.newBuilder()
76+
.setConsistency(0.75)
77+
.setPort(8081)
78+
.setStoreOnDisk(false)
79+
.setDataDir(dataDir)
80+
.build();
81+
assertTrue(Math.abs(0.75 - helper.getConsistency()) < TOLERANCE);
82+
assertTrue(helper.getProjectId().startsWith(PROJECT_ID_PREFIX));
83+
assertFalse(helper.isStoreOnDisk());
84+
assertEquals(8081, helper.getPort());
85+
assertEquals(dataDir, helper.getGcdPath());
86+
LocalDatastoreHelper incompleteHelper = LocalDatastoreHelper.newBuilder().build();
87+
assertTrue(Math.abs(0.9 - incompleteHelper.getConsistency()) < TOLERANCE);
88+
assertTrue(incompleteHelper.getProjectId().startsWith(PROJECT_ID_PREFIX));
89+
}
90+
91+
@Test
92+
public void testCreateWithToBuilder() throws IOException {
93+
LocalDatastoreHelper helper =
94+
LocalDatastoreHelper.newBuilder()
95+
.setConsistency(0.75)
96+
.setPort(8081)
97+
.setStoreOnDisk(false)
98+
.setDataDir(dataDir)
99+
.build();
100+
assertTrue(Math.abs(0.75 - helper.getConsistency()) < TOLERANCE);
101+
assertTrue(helper.getProjectId().startsWith(PROJECT_ID_PREFIX));
102+
assertFalse(helper.isStoreOnDisk());
103+
assertEquals(8081, helper.getPort());
104+
assertEquals(dataDir, helper.getGcdPath());
105+
LocalDatastoreHelper actualHelper = helper.toBuilder().build();
106+
assertLocalDatastoreHelpersEquivelent(helper, actualHelper);
107+
Path dataDir = Files.createTempDirectory("gcd_data_dir");
108+
actualHelper =
109+
helper
110+
.toBuilder()
111+
.setConsistency(0.85)
112+
.setPort(9091)
113+
.setStoreOnDisk(true)
114+
.setDataDir(dataDir)
115+
.build();
116+
assertTrue(Math.abs(0.85 - actualHelper.getConsistency()) < TOLERANCE);
117+
assertTrue(actualHelper.isStoreOnDisk());
118+
assertEquals(9091, actualHelper.getPort());
119+
assertEquals(dataDir, actualHelper.getGcdPath());
120+
LocalDatastoreHelper.deleteRecursively(dataDir);
121+
}
122+
57123
@Test
58124
public void testCreatePort() {
59125
LocalDatastoreHelper helper = LocalDatastoreHelper.create(0.75, 8888);
@@ -103,4 +169,31 @@ public void testStartStopReset() throws IOException, InterruptedException, Timeo
103169
assertNotNull(ex.getMessage());
104170
}
105171
}
172+
173+
@Test
174+
public void testStartStopResetWithBuilder()
175+
throws IOException, InterruptedException, TimeoutException {
176+
try {
177+
LocalDatastoreHelper helper = LocalDatastoreHelper.newBuilder().build();
178+
helper.start();
179+
Datastore datastore = helper.getOptions().getService();
180+
Key key = datastore.newKeyFactory().setKind("kind").newKey("name");
181+
datastore.put(Entity.newBuilder(key).build());
182+
assertNotNull(datastore.get(key));
183+
helper.reset();
184+
assertNull(datastore.get(key));
185+
helper.stop(Duration.ofMinutes(1));
186+
datastore.get(key);
187+
Assert.fail();
188+
} catch (DatastoreException ex) {
189+
assertNotNull(ex.getMessage());
190+
}
191+
}
192+
193+
public void assertLocalDatastoreHelpersEquivelent(
194+
LocalDatastoreHelper expected, LocalDatastoreHelper actual) {
195+
assertEquals(expected.getConsistency(), actual.getConsistency(), 0);
196+
assertEquals(expected.isStoreOnDisk(), actual.isStoreOnDisk());
197+
assertEquals(expected.getGcdPath(), actual.getGcdPath());
198+
}
106199
}

0 commit comments

Comments
 (0)