Skip to content

Commit 045569a

Browse files
committed
refactor: Introduce BaseAPI class for common functionality and update CloudRecording and ConvoAI APIs to utilize it, enhancing retry logic and path management
1 parent 246ba19 commit 045569a

17 files changed

+198
-168
lines changed

agora-rest-client-core/src/main/java/io/agora/rest/services/cloudrecording/CloudRecordingClientImpl.java

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,18 +31,24 @@ public class CloudRecordingClientImpl extends CloudRecordingClient {
3131

3232
private final MixScenario mixScenario;
3333

34+
private final static Integer MAX_ATTEMPTS = 3;
35+
36+
private final static String pathPrefix = "/v1/apps/%s/cloud_recording";
37+
3438
protected CloudRecordingClientImpl(Context context) {
35-
this.acquireResourceAPI = new AcquireResourceAPI(context);
36-
this.queryResourceAPI = new QueryResourceAPI(context);
37-
this.startResourceAPI = new StartResourceAPI(context);
38-
this.stopResourceAPI = new StopResourceAPI(context);
39-
this.updateResourceAPI = new UpdateResourceAPI(context);
39+
this.acquireResourceAPI = new AcquireResourceAPI(context, pathPrefix, MAX_ATTEMPTS);
40+
this.queryResourceAPI = new QueryResourceAPI(context, pathPrefix, MAX_ATTEMPTS);
41+
this.startResourceAPI = new StartResourceAPI(context, pathPrefix, MAX_ATTEMPTS);
42+
this.stopResourceAPI = new StopResourceAPI(context, pathPrefix, MAX_ATTEMPTS);
43+
this.updateResourceAPI = new UpdateResourceAPI(context, pathPrefix, MAX_ATTEMPTS);
4044

4145
this.individualScenario = new IndividualScenarioImpl(acquireResourceAPI, queryResourceAPI, startResourceAPI,
4246
updateResourceAPI, stopResourceAPI);
43-
this.webScenario = new WebScenarioImpl(acquireResourceAPI, queryResourceAPI, startResourceAPI, updateResourceAPI,
47+
this.webScenario = new WebScenarioImpl(acquireResourceAPI, queryResourceAPI, startResourceAPI,
48+
updateResourceAPI,
4449
stopResourceAPI);
45-
this.mixScenario = new MixScenarioImpl(acquireResourceAPI, queryResourceAPI, startResourceAPI, updateResourceAPI,
50+
this.mixScenario = new MixScenarioImpl(acquireResourceAPI, queryResourceAPI, startResourceAPI,
51+
updateResourceAPI,
4652
stopResourceAPI);
4753
}
4854

@@ -59,17 +65,17 @@ public Mono<QueryResourceRes> query(String resourceId, String sid, CloudRecordin
5965
}
6066

6167
public Mono<StopResourceRes> stop(String resourceId, String sid, CloudRecordingModeEnum mode,
62-
StopResourceReq request) {
68+
StopResourceReq request) {
6369
return stopResourceAPI.handle(resourceId, sid, mode, request);
6470
}
6571

6672
public Mono<UpdateResourceRes> update(String resourceId, String sid, CloudRecordingModeEnum mode,
67-
UpdateResourceReq request) {
73+
UpdateResourceReq request) {
6874
return updateResourceAPI.handle(resourceId, sid, mode, request);
6975
}
7076

7177
public Mono<UpdateLayoutResourceRes> updateLayout(String resourceId, String sid, CloudRecordingModeEnum mode,
72-
UpdateLayoutResourceReq request) {
78+
UpdateLayoutResourceReq request) {
7379
return updateResourceAPI.handleLayout(resourceId, sid, mode, request);
7480
}
7581

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
package io.agora.rest.services.cloudrecording.api;
22

33
import io.agora.rest.core.Context;
4+
import io.agora.rest.exception.AgoraNeedRetryException;
45
import io.agora.rest.services.cloudrecording.api.req.AcquireResourceReq;
56
import io.agora.rest.services.cloudrecording.api.res.AcquireResourceRes;
67
import io.netty.handler.codec.http.HttpMethod;
78
import reactor.core.publisher.Mono;
89

9-
public class AcquireResourceAPI {
10+
public class AcquireResourceAPI extends BaseAPI {
1011

11-
private final Context context;
12-
13-
public AcquireResourceAPI(Context context) {
14-
this.context = context;
12+
public AcquireResourceAPI(Context context, String pathPrefix, Integer maxAttempts) {
13+
super(context, pathPrefix, maxAttempts);
1514
}
1615

1716
public Mono<AcquireResourceRes> handle(AcquireResourceReq request) {
18-
String path = String.format("/v1/apps/%s/cloud_recording/acquire",
19-
this.context.getAgoraConfig().getAppId());
20-
return this.context.sendRequest(path, HttpMethod.POST, request, AcquireResourceRes.class);
17+
String path = String.format("%s/acquire",
18+
this.pathPrefix);
19+
return this.context.sendRequest(path, HttpMethod.POST, request, AcquireResourceRes.class)
20+
.retryWhen(customRetry(e -> e instanceof AgoraNeedRetryException));
2121
}
2222
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package io.agora.rest.services.cloudrecording.api;
2+
3+
import java.time.Duration;
4+
import java.util.function.Predicate;
5+
6+
import org.slf4j.Logger;
7+
import org.slf4j.LoggerFactory;
8+
9+
import io.agora.rest.core.Context;
10+
import reactor.util.retry.Retry;
11+
import reactor.util.retry.RetryBackoffSpec;
12+
13+
public abstract class BaseAPI {
14+
protected final Context context;
15+
16+
protected final Logger logger = LoggerFactory.getLogger(getClass());
17+
18+
protected final Integer maxAttempts;
19+
20+
protected final String pathPrefix;
21+
22+
public BaseAPI(Context context, String pathPrefix, Integer maxAttempts) {
23+
this.context = context;
24+
this.pathPrefix = pathPrefix;
25+
this.maxAttempts = maxAttempts;
26+
}
27+
28+
protected RetryBackoffSpec customRetry(Predicate<Throwable> retryPredicate) {
29+
return Retry.backoff(maxAttempts, Duration.ofSeconds(1)) // Maximum 5 retry attempts, initial 1-second interval
30+
.maxBackoff(Duration.ofSeconds(5)) // Maximum 5-second interval
31+
.filter(retryPredicate) // Retry condition
32+
.doBeforeRetry(retrySignal -> {
33+
long retryCount = retrySignal.totalRetries() + 1; // Current retry attempt number
34+
Duration nextBackoff = Duration.ofSeconds(retryCount); // Next retry interval
35+
logger.warn("Retry attempt: {}, next backoff: {}", retryCount, nextBackoff);
36+
}).onRetryExhaustedThrow((retryBackoffSpec, retrySignal) -> {
37+
logger.error("Retry exhausted: {}", retrySignal.totalRetries());
38+
return retrySignal.failure();
39+
});
40+
}
41+
}

agora-rest-client-core/src/main/java/io/agora/rest/services/cloudrecording/api/QueryResourceAPI.java

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

33
import io.agora.rest.core.Context;
44
import io.agora.rest.exception.AgoraJsonException;
5+
import io.agora.rest.exception.AgoraNeedRetryException;
56
import io.agora.rest.services.cloudrecording.api.res.QueryResourceRes;
67
import io.agora.rest.services.cloudrecording.enums.CloudRecordingModeEnum;
78
import io.netty.handler.codec.http.HttpMethod;
8-
import org.slf4j.Logger;
9-
import org.slf4j.LoggerFactory;
109
import reactor.core.publisher.Mono;
1110

12-
public class QueryResourceAPI {
11+
public class QueryResourceAPI extends BaseAPI {
1312

14-
private static final Logger logger = LoggerFactory.getLogger(QueryResourceAPI.class);
15-
16-
private final Context context;
17-
18-
public QueryResourceAPI(Context context) {
19-
this.context = context;
13+
public QueryResourceAPI(Context context, String pathPrefix, Integer maxAttempts) {
14+
super(context, pathPrefix, maxAttempts);
2015
}
2116

2217
public Mono<QueryResourceRes> handle(String resourceId, String sid, CloudRecordingModeEnum mode) {
23-
String path = String.format("/v1/apps/%s/cloud_recording/resourceid/%s/sid/%s/mode/%s/query",
24-
this.context.getAgoraConfig().getAppId(), resourceId, sid, mode.getMode());
18+
String path = String.format("%s/resourceid/%s/sid/%s/mode/%s/query",
19+
this.pathPrefix, resourceId, sid, mode.getMode());
2520
return this.context.sendRequest(path, HttpMethod.GET, null, QueryResourceRes.class)
21+
.retryWhen(customRetry(e -> e instanceof AgoraNeedRetryException))
2622
.handle((resp, sink) -> {
2723
try {
2824
resp.setServerResponse(mode);

agora-rest-client-core/src/main/java/io/agora/rest/services/cloudrecording/api/StartResourceAPI.java

Lines changed: 6 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -6,49 +6,20 @@
66
import io.agora.rest.services.cloudrecording.api.res.StartResourceRes;
77
import io.agora.rest.services.cloudrecording.enums.CloudRecordingModeEnum;
88
import io.netty.handler.codec.http.HttpMethod;
9-
import org.slf4j.Logger;
10-
import org.slf4j.LoggerFactory;
119
import reactor.core.publisher.Mono;
12-
import reactor.util.retry.Retry;
13-
import reactor.util.retry.RetryBackoffSpec;
1410

15-
import java.time.Duration;
16-
import java.util.function.Predicate;
11+
public class StartResourceAPI extends BaseAPI {
1712

18-
19-
public class StartResourceAPI {
20-
21-
private static final Logger logger = LoggerFactory.getLogger(StartResourceAPI.class);
22-
23-
private final Context context;
24-
25-
private final static int MAX_ATTEMPTS = 3;
26-
27-
public StartResourceAPI(Context context) {
28-
this.context = context;
13+
public StartResourceAPI(Context context, String pathPrefix, Integer maxAttempts) {
14+
super(context, pathPrefix, maxAttempts);
2915
}
3016

3117
public Mono<StartResourceRes> handle(String resourceId, CloudRecordingModeEnum mode, StartResourceReq request) {
32-
String path = String.format("/v1/apps/%s/cloud_recording/resourceid/%s/mode/%s/start",
33-
this.context.getAgoraConfig().getAppId(), resourceId, mode.getMode());
18+
String path = String.format("%s/resourceid/%s/mode/%s/start",
19+
this.pathPrefix, resourceId, mode.getMode());
3420

3521
return this.context.sendRequest(path, HttpMethod.POST, request, StartResourceRes.class)
36-
.retryWhen(customRetry(MAX_ATTEMPTS, e -> e instanceof AgoraNeedRetryException));
37-
}
38-
39-
40-
private RetryBackoffSpec customRetry(int maxAttempts, Predicate<Throwable> retryPredicate) {
41-
return Retry.backoff(maxAttempts, Duration.ofSeconds(1)) // Maximum 5 retry attempts, initial 1-second interval
42-
.maxBackoff(Duration.ofSeconds(5)) // Maximum 5-second interval
43-
.filter(retryPredicate) // Retry condition
44-
.doBeforeRetry(retrySignal -> {
45-
long retryCount = retrySignal.totalRetries() + 1; // Current retry attempt number
46-
Duration nextBackoff = Duration.ofSeconds(retryCount); // Next retry interval
47-
logger.warn("Retry attempt: {}, next backoff: {}", retryCount, nextBackoff);
48-
}).onRetryExhaustedThrow((retryBackoffSpec, retrySignal) -> {
49-
logger.error("Retry exhausted: {}", retrySignal.totalRetries());
50-
return retrySignal.failure();
51-
});
22+
.retryWhen(customRetry(e -> e instanceof AgoraNeedRetryException));
5223
}
5324

5425
}
Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,24 @@
11
package io.agora.rest.services.cloudrecording.api;
22

33
import io.agora.rest.core.Context;
4+
import io.agora.rest.exception.AgoraNeedRetryException;
45
import io.agora.rest.services.cloudrecording.api.req.StopResourceReq;
56
import io.agora.rest.services.cloudrecording.api.res.StopResourceRes;
67
import io.agora.rest.services.cloudrecording.enums.CloudRecordingModeEnum;
78
import io.netty.handler.codec.http.HttpMethod;
89
import reactor.core.publisher.Mono;
910

10-
public class StopResourceAPI {
11+
public class StopResourceAPI extends BaseAPI {
1112

12-
private final Context context;
13-
14-
public StopResourceAPI(Context context) {
15-
this.context = context;
13+
public StopResourceAPI(Context context, String pathPrefix, Integer maxAttempts) {
14+
super(context, pathPrefix, maxAttempts);
1615
}
1716

1817
public Mono<StopResourceRes> handle(String resourceId, String sid, CloudRecordingModeEnum mode,
19-
StopResourceReq request) {
20-
String path = String.format("/v1/apps/%s/cloud_recording/resourceid/%s/sid/%s/mode/%s/stop",
21-
this.context.getAgoraConfig().getAppId(), resourceId, sid, mode.getMode());
22-
return this.context.sendRequest(path, HttpMethod.POST, request, StopResourceRes.class);
18+
StopResourceReq request) {
19+
String path = String.format("%s/resourceid/%s/sid/%s/mode/%s/stop",
20+
this.pathPrefix, resourceId, sid, mode.getMode());
21+
return this.context.sendRequest(path, HttpMethod.POST, request, StopResourceRes.class)
22+
.retryWhen(customRetry(e -> e instanceof AgoraNeedRetryException));
2323
}
2424
}
Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package io.agora.rest.services.cloudrecording.api;
22

33
import io.agora.rest.core.Context;
4+
import io.agora.rest.exception.AgoraNeedRetryException;
45
import io.agora.rest.services.cloudrecording.api.req.UpdateLayoutResourceReq;
56
import io.agora.rest.services.cloudrecording.api.req.UpdateResourceReq;
67
import io.agora.rest.services.cloudrecording.api.res.UpdateLayoutResourceRes;
@@ -9,31 +10,31 @@
910
import io.netty.handler.codec.http.HttpMethod;
1011
import reactor.core.publisher.Mono;
1112

12-
public class UpdateResourceAPI {
13+
public class UpdateResourceAPI extends BaseAPI {
1314

14-
private final Context context;
15-
16-
public UpdateResourceAPI(Context context) {
17-
this.context = context;
15+
public UpdateResourceAPI(Context context, String pathPrefix, Integer maxAttempts) {
16+
super(context, pathPrefix, maxAttempts);
1817
}
1918

2019
public Mono<UpdateResourceRes> handle(String resourceId, String sid, CloudRecordingModeEnum mode,
21-
UpdateResourceReq request) {
22-
String path = String.format("/v1/apps/%s/cloud_recording/resourceid/%s/sid/%s/mode/%s/update",
23-
this.context.getAgoraConfig().getAppId(),
20+
UpdateResourceReq request) {
21+
String path = String.format("%s/resourceid/%s/sid/%s/mode/%s/update",
22+
this.pathPrefix,
2423
resourceId,
2524
sid,
2625
mode.getMode());
27-
return this.context.sendRequest(path, HttpMethod.POST, request, UpdateResourceRes.class);
26+
return this.context.sendRequest(path, HttpMethod.POST, request, UpdateResourceRes.class)
27+
.retryWhen(customRetry(e -> e instanceof AgoraNeedRetryException));
2828
}
2929

3030
public Mono<UpdateLayoutResourceRes> handleLayout(String resourceId, String sid, CloudRecordingModeEnum mode,
31-
UpdateLayoutResourceReq request) {
32-
String path = String.format("/v1/apps/%s/cloud_recording/resourceid/%s/sid/%s/mode/%s/updateLayout",
33-
this.context.getAgoraConfig().getAppId(),
31+
UpdateLayoutResourceReq request) {
32+
String path = String.format("%s/resourceid/%s/sid/%s/mode/%s/updateLayout",
33+
this.pathPrefix,
3434
resourceId,
3535
sid,
3636
mode.getMode());
37-
return this.context.sendRequest(path, HttpMethod.POST, request, UpdateLayoutResourceRes.class);
37+
return this.context.sendRequest(path, HttpMethod.POST, request, UpdateLayoutResourceRes.class)
38+
.retryWhen(customRetry(e -> e instanceof AgoraNeedRetryException));
3839
}
3940
}

agora-rest-client-core/src/main/java/io/agora/rest/services/convoai/ConvoAIClientImpl.java

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,16 +38,18 @@ public class ConvoAIClientImpl extends ConvoAIClient {
3838

3939
private final static String globalPrefixTpl = "/api/conversational-ai-agent/v2/projects/%s";
4040

41+
private final static Integer MAX_ATTEMPTS = 3;
42+
4143
protected ConvoAIClientImpl(Context context, ConvoAIServiceRegionEnum serviceRegionEnum) {
4244
String pathPrefix = getPathPrefix(context, serviceRegionEnum);
43-
joinConvoAIAPI = new JoinConvoAIAPI(context, pathPrefix);
44-
leaveConvoAIAPI = new LeaveConvoAIAPI(context, pathPrefix);
45-
listConvoAIAPI = new ListConvoAIAPI(context, pathPrefix);
46-
queryConvoAIAPI = new QueryConvoAIAPI(context, pathPrefix);
47-
updateConvoAIAPI = new UpdateConvoAIAPI(context, pathPrefix);
48-
historyConvoAIAPI = new HistoryConvoAIAPI(context, pathPrefix);
49-
interruptConvoAIAPI = new InterruptConvoAIAPI(context, pathPrefix);
50-
speakConvoAIAPI = new SpeakConvoAIAPI(context, pathPrefix);
45+
joinConvoAIAPI = new JoinConvoAIAPI(context, pathPrefix, MAX_ATTEMPTS);
46+
leaveConvoAIAPI = new LeaveConvoAIAPI(context, pathPrefix, MAX_ATTEMPTS);
47+
listConvoAIAPI = new ListConvoAIAPI(context, pathPrefix, MAX_ATTEMPTS);
48+
queryConvoAIAPI = new QueryConvoAIAPI(context, pathPrefix, MAX_ATTEMPTS);
49+
updateConvoAIAPI = new UpdateConvoAIAPI(context, pathPrefix, MAX_ATTEMPTS);
50+
historyConvoAIAPI = new HistoryConvoAIAPI(context, pathPrefix, MAX_ATTEMPTS);
51+
interruptConvoAIAPI = new InterruptConvoAIAPI(context, pathPrefix, MAX_ATTEMPTS);
52+
speakConvoAIAPI = new SpeakConvoAIAPI(context, pathPrefix, MAX_ATTEMPTS);
5153
}
5254

5355
private String getPathPrefix(Context context, ConvoAIServiceRegionEnum serviceRegionEnum) {
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package io.agora.rest.services.convoai.api;
2+
3+
import io.agora.rest.core.Context;
4+
import org.slf4j.Logger;
5+
import org.slf4j.LoggerFactory;
6+
import reactor.util.retry.Retry;
7+
import reactor.util.retry.RetryBackoffSpec;
8+
import java.time.Duration;
9+
import java.util.function.Predicate;
10+
11+
public abstract class BaseAPI {
12+
protected final Context context;
13+
14+
protected final Logger logger = LoggerFactory.getLogger(getClass());
15+
16+
protected final Integer maxAttempts;
17+
18+
protected final String pathPrefix;
19+
20+
public BaseAPI(Context context, String pathPrefix, Integer maxAttempts) {
21+
this.context = context;
22+
this.pathPrefix = pathPrefix;
23+
this.maxAttempts = maxAttempts;
24+
}
25+
26+
protected RetryBackoffSpec customRetry(Predicate<Throwable> retryPredicate) {
27+
return Retry.backoff(maxAttempts, Duration.ofSeconds(1)) // Maximum 5 retry attempts, initial 1-second interval
28+
.maxBackoff(Duration.ofSeconds(5)) // Maximum 5-second interval
29+
.filter(retryPredicate) // Retry condition
30+
.doBeforeRetry(retrySignal -> {
31+
long retryCount = retrySignal.totalRetries() + 1; // Current retry attempt number
32+
Duration nextBackoff = Duration.ofSeconds(retryCount); // Next retry interval
33+
logger.warn("Retry attempt: {}, next backoff: {}", retryCount, nextBackoff);
34+
}).onRetryExhaustedThrow((retryBackoffSpec, retrySignal) -> {
35+
logger.error("Retry exhausted: {}", retrySignal.totalRetries());
36+
return retrySignal.failure();
37+
});
38+
}
39+
}
Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,20 @@
11
package io.agora.rest.services.convoai.api;
22

33
import io.agora.rest.core.Context;
4+
import io.agora.rest.exception.AgoraNeedRetryException;
45
import io.agora.rest.services.convoai.res.HistoryConvoAIRes;
56
import io.netty.handler.codec.http.HttpMethod;
67
import reactor.core.publisher.Mono;
78

8-
public class HistoryConvoAIAPI {
9-
private final Context context;
9+
public class HistoryConvoAIAPI extends BaseAPI {
1010

11-
private final String pathPrefix;
12-
13-
public HistoryConvoAIAPI(Context context, String pathPrefix) {
14-
this.context = context;
15-
this.pathPrefix = pathPrefix;
11+
public HistoryConvoAIAPI(Context context, String pathPrefix, Integer maxAttempts) {
12+
super(context, pathPrefix, maxAttempts);
1613
}
1714

1815
public Mono<HistoryConvoAIRes> handle(String agentId) {
1916
String path = String.format("%s/agents/%s/history", pathPrefix, agentId);
20-
return this.context.sendRequest(path, HttpMethod.GET, null, HistoryConvoAIRes.class);
17+
return this.context.sendRequest(path, HttpMethod.GET, null, HistoryConvoAIRes.class)
18+
.retryWhen(customRetry(e -> e instanceof AgoraNeedRetryException));
2119
}
2220
}

0 commit comments

Comments
 (0)