Skip to content

Commit

Permalink
Merge pull request #15614 from cdapio/CDAP-20999-cherrypick
Browse files Browse the repository at this point in the history
[🍒][CDAP-20999] Encode schedule names in the schedule client
  • Loading branch information
itsankit-google authored Apr 25, 2024
2 parents ad25bcd + 5627f50 commit 3d75c90
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import com.google.gson.GsonBuilder;
import com.google.inject.Inject;
import io.cdap.cdap.api.schedule.Trigger;
import io.cdap.cdap.client.ScheduleClient;
import io.cdap.cdap.common.NotFoundException;
import io.cdap.cdap.common.ProgramNotFoundException;
import io.cdap.cdap.common.conf.Constants;
Expand Down Expand Up @@ -73,7 +74,8 @@ public ScheduleDetail get(ScheduleId scheduleId)
throws IOException, ScheduleNotFoundException, UnauthorizedException {
String url = String.format(
"namespaces/%s/apps/%s/schedules/%s",
scheduleId.getNamespace(), scheduleId.getApplication(), scheduleId.getSchedule());
scheduleId.getNamespace(), scheduleId.getApplication(),
ScheduleClient.getEncodedScheduleName(scheduleId.getSchedule()));
HttpRequest.Builder requestBuilder = remoteClient.requestBuilder(HttpMethod.GET, url);
HttpResponse httpResponse;
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,31 +29,49 @@
import io.cdap.cdap.proto.id.WorkflowId;
import io.cdap.cdap.test.XSlowTests;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Tests for {@link io.cdap.cdap.client.ServiceClient}.
*/
@Category(XSlowTests.class)
@RunWith(Parameterized.class)
public class ScheduleClientTestRun extends ClientTestBase {
private static final Logger LOG = LoggerFactory.getLogger(ScheduleClientTestRun.class);

private final NamespaceId namespace = NamespaceId.DEFAULT;
private final ApplicationId app = namespace.app(FakeApp.NAME);
private final WorkflowId workflow = app.workflow(FakeWorkflow.NAME);
private final ScheduleId schedule = app.schedule(FakeApp.TIME_SCHEDULE_NAME);
private final ScheduleId schedule;

private ScheduleClient scheduleClient;
private ApplicationClient appClient;

public ScheduleClientTestRun(String scheduleName) {
this.schedule = app.schedule(scheduleName);
}

@Parameterized.Parameters(name = "{index}: scheduleName = {0}")
public static Collection<String[]> data() {
Collection<String[]> params = new ArrayList<>();
params.add(new String[] { "someSchedule" });
params.add(new String[] { "some +-:?'` Schedule" });
params.add(new String[] { "No.10 - 0014002 AND No.16 0015006" });
return params;
}

@Before
public void setUp() throws Throwable {
super.setUp();
Expand All @@ -73,13 +91,17 @@ public void tearDown() throws Throwable {

@Test
public void testAll() throws Exception {
File appJar = createAppJarFile(FakeApp.class);
// deploy the app with time schedule
FakeApp.AppConfig config = new FakeApp.AppConfig(true, schedule.getSchedule(), null);
appClient.deploy(namespace, appJar, config);
List<ScheduleDetail> list = scheduleClient.listSchedules(workflow);
Assert.assertEquals(1, list.size());

ScheduleDetail timeSchedule = list.get(0);
ProtoTrigger.TimeTrigger timeTrigger = (ProtoTrigger.TimeTrigger) timeSchedule.getTrigger();

Assert.assertEquals(FakeApp.TIME_SCHEDULE_NAME, timeSchedule.getName());
Assert.assertEquals(schedule.getSchedule(), timeSchedule.getName());

Assert.assertEquals(FakeApp.SCHEDULE_CRON, timeTrigger.getCronExpression());

Expand Down Expand Up @@ -135,14 +157,14 @@ public void testScheduleChanges() throws Exception {
File appJar = createAppJarFile(FakeApp.class);

// deploy the app with time schedule
FakeApp.AppConfig config = new FakeApp.AppConfig(true, null, null);
FakeApp.AppConfig config = new FakeApp.AppConfig(true, schedule.getSchedule(), null);
appClient.deploy(namespace, appJar, config);
// now there should be one schedule
List<ScheduleDetail> list = scheduleClient.listSchedules(workflow);
Assert.assertEquals(1, list.size());

// test updating the schedule cron
config = new FakeApp.AppConfig(true, FakeApp.TIME_SCHEDULE_NAME, "0 2 1 1 *");
config = new FakeApp.AppConfig(true, schedule.getSchedule(), "0 2 1 1 *");
appClient.deploy(namespace, appJar, config);
list = scheduleClient.listSchedules(workflow);
Assert.assertEquals(1, list.size());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -704,7 +704,7 @@ public void addSchedule(ApplicationId app, ScheduleDetail scheduleDetail)
UnauthorizedException, BadRequestException {
String path = String.format("apps/%s/versions/%s/schedules/%s", app.getApplication(),
app.getVersion(),
scheduleDetail.getName());
ScheduleClient.getEncodedScheduleName(scheduleDetail.getName()));
HttpResponse response = restClient.execute(HttpMethod.PUT,
config.resolveNamespacedURLV3(app.getParent(), path),
GSON.toJson(scheduleDetail), ImmutableMap.<String, String>of(),
Expand Down
19 changes: 13 additions & 6 deletions cdap-client/src/main/java/io/cdap/cdap/client/ScheduleClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,12 @@
import io.cdap.common.http.HttpResponse;
import io.cdap.common.http.ObjectResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Type;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
Expand Down Expand Up @@ -134,7 +137,7 @@ public void suspend(ScheduleId scheduleId)
throws IOException, UnauthenticatedException, NotFoundException,
UnauthorizedException {
String path = String.format("apps/%s/schedules/%s/suspend", scheduleId.getApplication(),
scheduleId.getSchedule());
getEncodedScheduleName(scheduleId.getSchedule()));
URL url = config.resolveNamespacedURLV3(scheduleId.getNamespaceId(), path);
HttpResponse response = restClient.execute(HttpMethod.POST, url, config.getAccessToken(),
HttpURLConnection.HTTP_NOT_FOUND);
Expand All @@ -147,7 +150,7 @@ public void resume(ScheduleId scheduleId)
throws IOException, UnauthenticatedException, NotFoundException,
UnauthorizedException {
String path = String.format("apps/%s/schedules/%s/resume", scheduleId.getApplication(),
scheduleId.getSchedule());
getEncodedScheduleName(scheduleId.getSchedule()));
URL url = config.resolveNamespacedURLV3(scheduleId.getNamespaceId(), path);
HttpResponse response = restClient.execute(HttpMethod.POST, url, config.getAccessToken(),
HttpURLConnection.HTTP_NOT_FOUND);
Expand All @@ -165,7 +168,7 @@ public void delete(ScheduleId scheduleId)
throws IOException, UnauthenticatedException, NotFoundException,
UnauthorizedException {
String path = String.format("apps/%s/schedules/%s", scheduleId.getApplication(),
scheduleId.getSchedule());
getEncodedScheduleName(scheduleId.getSchedule()));
URL url = config.resolveNamespacedURLV3(scheduleId.getNamespaceId(), path);
HttpResponse response = restClient.execute(HttpMethod.DELETE, url, config.getAccessToken(),
HttpURLConnection.HTTP_NOT_FOUND);
Expand All @@ -178,7 +181,7 @@ public String getStatus(ScheduleId scheduleId)
throws IOException, UnauthenticatedException, NotFoundException,
UnauthorizedException {
String path = String.format("apps/%s/schedules/%s/status", scheduleId.getApplication(),
scheduleId.getSchedule());
getEncodedScheduleName(scheduleId.getSchedule()));
URL url = config.resolveNamespacedURLV3(scheduleId.getParent().getParent(), path);
HttpResponse response = restClient.execute(HttpMethod.GET, url, config.getAccessToken(),
HttpURLConnection.HTTP_NOT_FOUND);
Expand Down Expand Up @@ -219,7 +222,7 @@ private void doAdd(ScheduleId scheduleId, String json) throws IOException,
UnauthenticatedException, NotFoundException, UnauthorizedException, AlreadyExistsException {

String path = String.format("apps/%s/schedules/%s", scheduleId.getApplication(),
scheduleId.getSchedule());
getEncodedScheduleName(scheduleId.getSchedule()));
URL url = config.resolveNamespacedURLV3(scheduleId.getNamespaceId(), path);
HttpRequest request = HttpRequest.put(url).withBody(json).build();
HttpResponse response = restClient.execute(request, config.getAccessToken(),
Expand All @@ -236,7 +239,7 @@ private void doUpdate(ScheduleId scheduleId, String json) throws IOException,
UnauthenticatedException, NotFoundException, UnauthorizedException, AlreadyExistsException {

String path = String.format("apps/%s/schedules/%s/update", scheduleId.getApplication(),
scheduleId.getSchedule());
getEncodedScheduleName(scheduleId.getSchedule()));
URL url = config.resolveNamespacedURLV3(scheduleId.getNamespaceId(), path);
HttpRequest request = HttpRequest.post(url).withBody(json).build();
HttpResponse response = restClient.execute(request, config.getAccessToken(),
Expand All @@ -263,4 +266,8 @@ private List<ScheduleDetail> doList(WorkflowId workflow)
return objectResponse.getResponseObject();
}

public static String getEncodedScheduleName(String scheduleName)
throws UnsupportedEncodingException {
return URLEncoder.encode(scheduleName, StandardCharsets.UTF_8.toString()).replace("+", "%20");
}
}

0 comments on commit 3d75c90

Please sign in to comment.