Skip to content

Commit bb09431

Browse files
authored
Clone support (#100)
* transplanted clone support from astra-sdk-java, verbatim - it builds. No tests yet * adjust AbstractDevopsApiTest to access DEV when needed; add (manual) clone testing
1 parent 3d28a63 commit bb09431

8 files changed

Lines changed: 806 additions & 2 deletions

File tree

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
package com.dtsx.astra.sdk.db;
2+
3+
import com.dtsx.astra.sdk.AbstractApiClient;
4+
import com.dtsx.astra.sdk.db.domain.Database;
5+
import com.dtsx.astra.sdk.db.domain.DatabaseCloneStatus;
6+
import com.dtsx.astra.sdk.utils.ApiLocator;
7+
import com.dtsx.astra.sdk.utils.ApiResponseHttp;
8+
import com.dtsx.astra.sdk.utils.Assert;
9+
import com.dtsx.astra.sdk.utils.AstraEnvironment;
10+
import com.dtsx.astra.sdk.utils.JsonUtils;
11+
12+
import java.net.HttpURLConnection;
13+
import java.net.URLEncoder;
14+
import java.nio.charset.StandardCharsets;
15+
import java.util.stream.Stream;
16+
17+
/**
18+
* Delegate operation on DB cloning
19+
*/
20+
public class DbCloneClient extends AbstractApiClient {
21+
22+
/** Clone path component, part I. */
23+
public static final String PATH_CLONE_1 = "/databases/";
24+
25+
/** Clone path component, part II. */
26+
public static final String PATH_CLONE_2 = "/cloneFrom/";
27+
28+
/** Get-clone-status path component, part I. */
29+
public static final String PATH_GETSTATUS_1 = "/databases/";
30+
31+
/** Get-clone-status path component, part II. */
32+
public static final String PATH_GETSTATUS_2 = "/cloneStatus/";
33+
34+
/**
35+
* unique db identifier.
36+
*/
37+
private final Database db;
38+
39+
/**
40+
* As immutable object use builder to initiate the object.
41+
*
42+
* @param token
43+
* authenticated token
44+
* @param databaseId
45+
* database identifier
46+
*/
47+
public DbCloneClient(String token, String databaseId) {
48+
this(token, AstraEnvironment.PROD, databaseId);
49+
}
50+
51+
/**
52+
* As immutable object use builder to initiate the object.
53+
*
54+
* @param env
55+
* define target environment to be used
56+
* @param token
57+
* authenticated token
58+
* @param databaseId
59+
* database identifier
60+
*/
61+
public DbCloneClient(String token, AstraEnvironment env, String databaseId) {
62+
super(token, env);
63+
Assert.hasLength(databaseId, "databaseId");
64+
this.db = new DbOpsClient(token, env, databaseId).get();
65+
}
66+
67+
/** {@inheritDoc} */
68+
@Override
69+
public String getServiceName() {
70+
return "db.clone";
71+
}
72+
73+
/**
74+
* Clone database from a source database using a snapshot.
75+
*
76+
* @param sourceDbId
77+
* ID of the source database
78+
* @param databaseSnapshotId
79+
* ID of the snapshot to clone from
80+
* @return clone status
81+
*/
82+
public DatabaseCloneStatus cloneFrom(String sourceDbId, String databaseSnapshotId) {
83+
return cloneFrom(sourceDbId, databaseSnapshotId, null);
84+
}
85+
86+
/**
87+
* Clone database from a source database using a snapshot.
88+
*
89+
* @param sourceDbId
90+
* ID of the source database
91+
* @param databaseSnapshotId
92+
* ID of the snapshot to clone from
93+
* @param sourceRegion
94+
* Source region (optional)
95+
* @return clone status
96+
*/
97+
public DatabaseCloneStatus cloneFrom(String sourceDbId, String databaseSnapshotId, String sourceRegion) {
98+
Assert.hasLength(sourceDbId, "sourceDbId");
99+
Assert.hasLength(databaseSnapshotId, "databaseSnapshotId");
100+
101+
// Build URL with query parameters
102+
String url = getEndpointCloneFrom(sourceDbId) +
103+
"?snapshotID=" + URLEncoder.encode(databaseSnapshotId, StandardCharsets.UTF_8);
104+
105+
if (sourceRegion != null && !sourceRegion.isEmpty()) {
106+
url += "&sourceRegion=" + URLEncoder.encode(sourceRegion, StandardCharsets.UTF_8);
107+
}
108+
109+
// Fire POST request
110+
ApiResponseHttp res = POST(url, getOperationName("cloneFrom"));
111+
112+
// Parse and return response
113+
return JsonUtils.unmarshallBean(res.getBody(), DatabaseCloneStatus.class);
114+
}
115+
116+
/**
117+
* Get the status of a clone operation.
118+
*
119+
* @param operationId
120+
* ID of the clone operation
121+
* @return clone status
122+
*/
123+
public DatabaseCloneStatus getCloneStatus(String operationId) {
124+
Assert.hasLength(operationId, "operationId");
125+
126+
// Build URL
127+
String url = getEndpointGetCloneStatus(operationId);
128+
129+
// Fire GET request
130+
ApiResponseHttp res = GET(url, getOperationName("getCloneStatus"));
131+
132+
// Parse and return response
133+
return JsonUtils.unmarshallBean(res.getBody(), DatabaseCloneStatus.class);
134+
}
135+
136+
/**
137+
* Endpoint to initiate DB clone
138+
*
139+
* @param sourceDbId
140+
* ID of the source database
141+
* @return request endpoint
142+
*/
143+
private String getEndpointCloneFrom(String sourceDbId) {
144+
return ApiLocator.getApiDevopsEndpoint(environment) + PATH_CLONE_1 + db.getId() + PATH_CLONE_2 + sourceDbId;
145+
}
146+
147+
/**
148+
* Endpoint to retrieve a DB-clone status (by operation ID)
149+
*
150+
* @param operationId
151+
* ID of the clone operation (as obtained e.g. with a cloneFrom previous invocation)
152+
* @return request endpoint
153+
*/
154+
private String getEndpointGetCloneStatus(String operationId) {
155+
return ApiLocator.getApiDevopsEndpoint(environment) + PATH_GETSTATUS_1 + db.getId() + PATH_GETSTATUS_2 + operationId;
156+
}
157+
158+
}

‎astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/db/DbOpsClient.java‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,32 @@ public DbPrivateLinksClient privateLink() {
409409
return new DbPrivateLinksClient(token, environment, databaseId);
410410
}
411411

412+
// ---------------------------------
413+
// ---- Clone ----
414+
// ---------------------------------
415+
416+
/**
417+
* Delegate clone operation in a dedicated class
418+
*
419+
* @return clone client
420+
*/
421+
public DbCloneClient clone() {
422+
return new DbCloneClient(token, environment, databaseId);
423+
}
424+
425+
// ---------------------------------
426+
// ---- Snapshots ----
427+
// ---------------------------------
428+
429+
/**
430+
* Delegate snapshots operation in a dedicated class
431+
*
432+
* @return snapshots client
433+
*/
434+
public DbSnapshotsClient snapshots() {
435+
return new DbSnapshotsClient(token, environment, databaseId);
436+
}
437+
412438
// ---------------------------------
413439
// ---- Utilities ----
414440
// ---------------------------------
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
package com.dtsx.astra.sdk.db;
2+
3+
import com.dtsx.astra.sdk.AbstractApiClient;
4+
import com.dtsx.astra.sdk.db.domain.Database;
5+
import com.dtsx.astra.sdk.db.domain.DatabaseSnapshot;
6+
import com.dtsx.astra.sdk.db.domain.DatabaseSnapshotsResponse;
7+
import com.dtsx.astra.sdk.utils.ApiLocator;
8+
import com.dtsx.astra.sdk.utils.ApiResponseHttp;
9+
import com.dtsx.astra.sdk.utils.Assert;
10+
import com.dtsx.astra.sdk.utils.AstraEnvironment;
11+
import com.dtsx.astra.sdk.utils.JsonUtils;
12+
13+
import java.net.HttpURLConnection;
14+
import java.net.URLEncoder;
15+
import java.nio.charset.StandardCharsets;
16+
import java.util.stream.Stream;
17+
18+
/**
19+
* Delegate operation on snapshots
20+
*/
21+
public class DbSnapshotsClient extends AbstractApiClient {
22+
23+
/** Path for listing snapshots, part I. */
24+
public static final String PATH_SNAPSHOTS_1 = "/databases/";
25+
26+
/** Path for listing snapshots, part II. */
27+
public static final String PATH_SNAPSHOTS_2 = "/snapshots";
28+
29+
/**
30+
* unique db identifier.
31+
*/
32+
private final Database db;
33+
34+
/**
35+
* As immutable object use builder to initiate the object.
36+
*
37+
* @param token
38+
* authenticated token
39+
* @param databaseId
40+
* database identifier
41+
*/
42+
public DbSnapshotsClient(String token, String databaseId) {
43+
this(token, AstraEnvironment.PROD, databaseId);
44+
}
45+
46+
/**
47+
* As immutable object use builder to initiate the object.
48+
*
49+
* @param env
50+
* define target environment to be used
51+
* @param token
52+
* authenticated token
53+
* @param databaseId
54+
* database identifier
55+
*/
56+
public DbSnapshotsClient(String token, AstraEnvironment env, String databaseId) {
57+
super(token, env);
58+
Assert.hasLength(databaseId, "databaseId");
59+
this.db = new DbOpsClient(token, env, databaseId).get();
60+
}
61+
62+
/** {@inheritDoc} */
63+
@Override
64+
public String getServiceName() {
65+
return "db.snapshots";
66+
}
67+
68+
/**
69+
* Get snapshots with filters.
70+
*
71+
* @param from
72+
* start time (ISO 8601 format)
73+
* @param to
74+
* end time (ISO 8601 format)
75+
* @param region
76+
* region name
77+
* @return list of snapshots.
78+
*/
79+
public Stream<DatabaseSnapshot> find(String from, String to, String region) {
80+
// Build URL with query parameters
81+
StringBuilder url = new StringBuilder(getEndpointSnapshots());
82+
boolean hasParams = false;
83+
84+
if (region != null) {
85+
url.append("?sourceRegion=").append(URLEncoder.encode(region, StandardCharsets.UTF_8));
86+
hasParams = true;
87+
}
88+
89+
if (from != null) {
90+
url.append(hasParams ? "&" : "?").append("from=").append(URLEncoder.encode(from, StandardCharsets.UTF_8));
91+
hasParams = true;
92+
}
93+
94+
if (to != null) {
95+
url.append(hasParams ? "&" : "?").append("to=").append(URLEncoder.encode(to, StandardCharsets.UTF_8));
96+
}
97+
98+
// Invoke endpoint
99+
ApiResponseHttp res = GET(url.toString(), getOperationName("find"));
100+
101+
// Handle response
102+
if (HttpURLConnection.HTTP_NOT_FOUND == res.getCode()) {
103+
return Stream.of();
104+
} else {
105+
DatabaseSnapshotsResponse response = JsonUtils.unmarshallBean(res.getBody(), DatabaseSnapshotsResponse.class);
106+
return response.getSnapshots() != null ? response.getSnapshots().stream() : Stream.of();
107+
}
108+
}
109+
110+
/**
111+
* Get all snapshots.
112+
*
113+
* @return list of snapshots.
114+
*/
115+
public Stream<DatabaseSnapshot> findAll() {
116+
return find(null, null, null);
117+
}
118+
119+
// TODO: add other overloads here
120+
121+
/**
122+
* Endpoint to access snapshots of a db
123+
*
124+
* @return database endpoint
125+
*/
126+
private String getEndpointSnapshots() {
127+
return ApiLocator.getApiDevopsEndpoint(environment) + PATH_SNAPSHOTS_1 + db.getId() + PATH_SNAPSHOTS_2;
128+
}
129+
130+
}

0 commit comments

Comments
 (0)