diff --git a/CHANGELOG.md b/CHANGELOG.md index cfc819f..78e4aaa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.3.2 - 2025-04-29 +* [enhancement] Use new endpoint rest/api/latest/search/jql +* PR [#88](https://github.com/treasure-data/embulk-input-jira/pull/88) + ## 0.3.1 - 2023-05-19 * [enhancement] Update library, minor code refactoring * PR [#78](https://github.com/treasure-data/embulk-input-jira/pull/78) diff --git a/build.gradle b/build.gradle index 70213e3..09f640e 100644 --- a/build.gradle +++ b/build.gradle @@ -12,7 +12,7 @@ repositories { mavenCentral() } -version = "0.3.1" +version = "0.3.2" group = "com.treasuredata.embulk.plugins" description = "JIRA Embulk input plugin." diff --git a/src/main/java/org/embulk/input/jira/Constant.java b/src/main/java/org/embulk/input/jira/Constant.java index 417ac85..41932ae 100644 --- a/src/main/java/org/embulk/input/jira/Constant.java +++ b/src/main/java/org/embulk/input/jira/Constant.java @@ -1,5 +1,7 @@ package org.embulk.input.jira; +import com.google.gson.Gson; + public final class Constant { public static final int MAX_RESULTS = 50; @@ -12,7 +14,8 @@ public final class Constant public static final String DEFAULT_TIMESTAMP_PATTERN = "%Y-%m-%dT%H:%M:%S.%L%z"; public static final String CREDENTIAL_URI_PATH = "rest/api/latest/myself"; - public static final String SEARCH_URI_PATH = "rest/api/latest/search"; + public static final String SEARCH_URI_PATH = "rest/api/latest/search/jql"; + public static final Gson GSON = new Gson(); private Constant(){} } diff --git a/src/main/java/org/embulk/input/jira/JiraInputPlugin.java b/src/main/java/org/embulk/input/jira/JiraInputPlugin.java index 7055c34..8c04c28 100644 --- a/src/main/java/org/embulk/input/jira/JiraInputPlugin.java +++ b/src/main/java/org/embulk/input/jira/JiraInputPlugin.java @@ -37,7 +37,6 @@ import java.util.SortedSet; import java.util.TreeSet; -import static org.embulk.input.jira.Constant.GUESS_RECORDS_COUNT; import static org.embulk.input.jira.Constant.MAX_RESULTS; import static org.embulk.input.jira.Constant.PREVIEW_RECORDS_COUNT; @@ -147,20 +146,22 @@ public TaskReport run(final TaskSource taskSource, jiraClient.checkUserCredentials(task); try (final PageBuilder pageBuilder = getPageBuilder(schema, output)) { if (isPreview()) { - final List issues = jiraClient.searchIssues(task, 0, PREVIEW_RECORDS_COUNT); + final SearchResult result = jiraClient.searchIssues(task, null, PREVIEW_RECORDS_COUNT); + final List issues = JiraUtil.fromSearchResult(result); issues.forEach(issue -> JiraUtil.addRecord(issue, schema, task, pageBuilder)); } else { - int currentPage = 0; - final int totalCount = jiraClient.getTotalCount(task); - final int totalPage = JiraUtil.calculateTotalPage(totalCount, MAX_RESULTS); - LOGGER.info(String.format("Total pages (%d)", totalPage)); - while (currentPage < totalPage) { - LOGGER.info(String.format("Fetching page %d/%d", (currentPage + 1), totalPage)); - final List issues = jiraClient.searchIssues(task, (currentPage * MAX_RESULTS), MAX_RESULTS); + int totalRecords = 0; + String nextPageToken = null; + do { + final SearchResult result = jiraClient.searchIssues(task, nextPageToken, MAX_RESULTS); + nextPageToken = result.getNextPageToken(); + final List issues = JiraUtil.fromSearchResult(result); + totalRecords += issues.size(); issues.forEach(issue -> JiraUtil.addRecord(issue, schema, task, pageBuilder)); - currentPage++; + LOGGER.info(String.format("Ingested %d items", totalRecords)); } + while (nextPageToken != null); } pageBuilder.finish(); } @@ -181,7 +182,8 @@ public ConfigDiff guess(final ConfigSource config) private List getGuessedColumns(final JiraClient jiraClient, final PluginTask task) { - final List issues = jiraClient.searchIssues(task, 0, GUESS_RECORDS_COUNT); + final SearchResult result = jiraClient.searchIssues(task, null, PREVIEW_RECORDS_COUNT); + final List issues = JiraUtil.fromSearchResult(result); if (issues.isEmpty()) { throw new ConfigException("Could not guess schema due to empty data set"); } diff --git a/src/main/java/org/embulk/input/jira/SearchResult.java b/src/main/java/org/embulk/input/jira/SearchResult.java new file mode 100644 index 0000000..e2eb87e --- /dev/null +++ b/src/main/java/org/embulk/input/jira/SearchResult.java @@ -0,0 +1,23 @@ +package org.embulk.input.jira; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; + +public class SearchResult +{ + private JsonElement issues; + private String nextPageToken; + + public JsonElement getIssues() + { + if (issues == null || issues.isJsonNull()) { + issues = new JsonArray(); + } + return issues; + } + + public String getNextPageToken() + { + return nextPageToken; + } +} diff --git a/src/main/java/org/embulk/input/jira/client/JiraClient.java b/src/main/java/org/embulk/input/jira/client/JiraClient.java index b5bd8b6..0c10d64 100644 --- a/src/main/java/org/embulk/input/jira/client/JiraClient.java +++ b/src/main/java/org/embulk/input/jira/client/JiraClient.java @@ -6,6 +6,8 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.google.gson.JsonPrimitive; + +import org.apache.commons.lang3.StringUtils; import org.apache.http.HttpStatus; import org.apache.http.client.config.CookieSpecs; import org.apache.http.client.config.RequestConfig; @@ -18,8 +20,8 @@ import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.util.EntityUtils; import org.embulk.config.ConfigException; -import org.embulk.input.jira.Issue; import org.embulk.input.jira.JiraInputPlugin.PluginTask; +import org.embulk.input.jira.SearchResult; import org.embulk.input.jira.util.JiraException; import org.embulk.input.jira.util.JiraUtil; import org.embulk.util.retryhelper.RetryExecutor; @@ -31,18 +33,13 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; -import java.util.Map.Entry; import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.StreamSupport; - import static java.util.Base64.getEncoder; import static org.apache.http.HttpHeaders.ACCEPT; import static org.apache.http.HttpHeaders.AUTHORIZATION; import static org.apache.http.HttpHeaders.CONTENT_TYPE; +import static org.embulk.input.jira.Constant.GSON; import static org.embulk.input.jira.Constant.HTTP_TIMEOUT; -import static org.embulk.input.jira.Constant.MIN_RESULTS; public class JiraClient { @@ -64,31 +61,12 @@ public void checkUserCredentials(final PluginTask task) } } - public List searchIssues(final PluginTask task, final int startAt, final int maxResults) - { - final String response = searchJiraAPI(task, startAt, maxResults); - final JsonObject result = new JsonParser().parse(response).getAsJsonObject(); - return StreamSupport.stream(result.get("issues").getAsJsonArray().spliterator(), false) - .map(jsonElement -> { - final JsonObject json = jsonElement.getAsJsonObject(); - final JsonObject fields = json.get("fields").getAsJsonObject(); - final Set> entries = fields.entrySet(); - json.remove("fields"); - // Merged all properties in fields to the object - for (final Entry entry : entries) { - json.add(entry.getKey(), entry.getValue()); - } - return new Issue(json); - }) - .collect(Collectors.toList()); - } - - public int getTotalCount(final PluginTask task) + public SearchResult searchIssues(final PluginTask task, final String nextPageToken, final int maxResults) { - return new JsonParser().parse(searchJiraAPI(task, 0, MIN_RESULTS)).getAsJsonObject().get("total").getAsInt(); + return GSON.fromJson(searchJiraAPI(task, nextPageToken, maxResults), SearchResult.class); } - private String searchJiraAPI(final PluginTask task, final int startAt, final int maxResults) + private String searchJiraAPI(final PluginTask task, final String nextPageToken, final int maxResults) { try { return RetryExecutor.builder() @@ -101,7 +79,7 @@ private String searchJiraAPI(final PluginTask task, final int startAt, final int @Override public String call() throws Exception { - return authorizeAndRequest(task, JiraUtil.buildSearchUrl(task.getUri()), createSearchIssuesBody(task, startAt, maxResults)); + return authorizeAndRequest(task, JiraUtil.buildSearchUrl(task.getUri()), createSearchIssuesBody(task, nextPageToken, maxResults)); } @Override @@ -243,12 +221,14 @@ private HttpRequestBase createGetRequest(final PluginTask task, final String url return request; } - private String createSearchIssuesBody(final PluginTask task, final int startAt, final int maxResults) + private String createSearchIssuesBody(final PluginTask task, final String nextPageToken, final int maxResults) { final JsonObject body = new JsonObject(); final Optional jql = task.getJQL(); body.add("jql", new JsonPrimitive(jql.orElse(""))); - body.add("startAt", new JsonPrimitive(startAt)); + if (StringUtils.isNotEmpty(nextPageToken)) { + body.add("nextPageToken", new JsonPrimitive(nextPageToken)); + } body.add("maxResults", new JsonPrimitive(maxResults)); final JsonArray fields = new JsonArray(); fields.add("*all"); diff --git a/src/main/java/org/embulk/input/jira/util/JiraUtil.java b/src/main/java/org/embulk/input/jira/util/JiraUtil.java index e4c2b67..e5e50f3 100644 --- a/src/main/java/org/embulk/input/jira/util/JiraUtil.java +++ b/src/main/java/org/embulk/input/jira/util/JiraUtil.java @@ -13,6 +13,7 @@ import org.embulk.config.ConfigSource; import org.embulk.input.jira.Issue; import org.embulk.input.jira.JiraInputPlugin.PluginTask; +import org.embulk.input.jira.SearchResult; import org.embulk.spi.Column; import org.embulk.spi.ColumnVisitor; import org.embulk.spi.PageBuilder; @@ -27,6 +28,8 @@ import java.time.Instant; import java.util.LinkedHashMap; import java.util.List; +import java.util.Map.Entry; +import java.util.Set; import java.util.stream.Collectors; import java.util.stream.StreamSupport; @@ -277,4 +280,21 @@ public static LinkedHashMap toLinkedHashMap(final JsonObject flt } return result; } + + public static List fromSearchResult(final SearchResult result) + { + return StreamSupport.stream(result.getIssues().getAsJsonArray().spliterator(), false) + .map(jsonElement -> { + final JsonObject json = jsonElement.getAsJsonObject(); + final JsonObject fields = json.get("fields").getAsJsonObject(); + final Set> entries = fields.entrySet(); + json.remove("fields"); + // Merged all properties in fields to the object + for (final Entry entry : entries) { + json.add(entry.getKey(), entry.getValue()); + } + return new Issue(json); + }) + .collect(Collectors.toList()); + } } diff --git a/src/test/java/org/embulk/input/jira/JiraInputPluginTest.java b/src/test/java/org/embulk/input/jira/JiraInputPluginTest.java index 6cfbe5c..3ef03c8 100644 --- a/src/test/java/org/embulk/input/jira/JiraInputPluginTest.java +++ b/src/test/java/org/embulk/input/jira/JiraInputPluginTest.java @@ -117,8 +117,8 @@ public void test_runDynamicSchema_withResult() throws IOException .thenReturn(new StringEntity(searchResponse.get("body").toString())); plugin.transaction(TestHelpers.dynamicSchemaConfig(), new Control()); - // Check credential 1 + getTotal 1 + loadData 2 - verify(jiraClient, times(4)).createHttpClient(); + // Check credential 1 + loadData 2 + verify(jiraClient, times(3)).createHttpClient(); verify(pageBuilder, times(1)).addRecord(); verify(pageBuilder, times(1)).finish(); } @@ -137,8 +137,8 @@ public void test_run_with1RecordsResult() throws IOException .thenReturn(new StringEntity(searchResponse.get("body").toString())); plugin.transaction(config, new Control()); - // Check credential 1 + getTotal 1 + loadData 1 - verify(jiraClient, times(3)).createHttpClient(); + // Check credential 1 + loadData 1 + verify(jiraClient, times(2)).createHttpClient(); verify(pageBuilder, times(1)).addRecord(); verify(pageBuilder, times(1)).finish(); } @@ -148,18 +148,21 @@ public void test_run_with2PagesResult() throws IOException { final JsonObject authorizeResponse = data.get("authenticateSuccess").getAsJsonObject(); final JsonObject searchResponse = data.get("2PagesResult").getAsJsonObject(); + final JsonObject emptyResponse = data.get("emptyResult").getAsJsonObject(); when(statusLine.getStatusCode()) .thenReturn(authorizeResponse.get("statusCode").getAsInt()) - .thenReturn(searchResponse.get("statusCode").getAsInt()); + .thenReturn(searchResponse.get("statusCode").getAsInt()) + .thenReturn(emptyResponse.get("statusCode").getAsInt()); when(response.getEntity()) .thenReturn(new StringEntity(authorizeResponse.get("body").toString())) - .thenReturn(new StringEntity(searchResponse.get("body").toString())); + .thenReturn(new StringEntity(searchResponse.get("body").toString())) + .thenReturn(new StringEntity(emptyResponse.get("body").toString())); plugin.transaction(config, new Control()); - // Check credential 1 + getTotal 1 + loadData 2 - verify(jiraClient, times(4)).createHttpClient(); - verify(pageBuilder, times(2)).addRecord(); + // Check credential 1 + loadData 2 + verify(jiraClient, times(3)).createHttpClient(); + verify(pageBuilder, times(1)).addRecord(); verify(pageBuilder, times(1)).finish(); } diff --git a/src/test/java/org/embulk/input/jira/client/JiraClientTest.java b/src/test/java/org/embulk/input/jira/client/JiraClientTest.java index 9562ade..c011d23 100644 --- a/src/test/java/org/embulk/input/jira/client/JiraClientTest.java +++ b/src/test/java/org/embulk/input/jira/client/JiraClientTest.java @@ -12,7 +12,7 @@ import org.embulk.input.jira.Issue; import org.embulk.input.jira.JiraInputPlugin.PluginTask; import org.embulk.input.jira.TestHelpers; - +import org.embulk.input.jira.util.JiraUtil; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -24,8 +24,6 @@ import static org.embulk.input.jira.JiraInputPlugin.CONFIG_MAPPER; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThrows; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; public class JiraClientTest @@ -35,9 +33,9 @@ public class JiraClientTest private JiraClient jiraClient; private PluginTask task; - private CloseableHttpClient client = Mockito.mock(CloseableHttpClient.class); + private final CloseableHttpClient client = Mockito.mock(CloseableHttpClient.class); private CloseableHttpResponse response = Mockito.mock(CloseableHttpResponse.class); - private StatusLine statusLine = Mockito.mock(StatusLine.class); + private final StatusLine statusLine = Mockito.mock(StatusLine.class); private JsonObject data; @Before @@ -57,10 +55,10 @@ public void setUp() throws IOException @Test public void test_checkUserCredentials_success() throws IOException { - String dataName = "credentialSuccess"; - JsonObject messageResponse = data.get(dataName).getAsJsonObject(); - int statusCode = messageResponse.get("statusCode").getAsInt(); - String body = messageResponse.get("body").toString(); + final String dataName = "credentialSuccess"; + final JsonObject messageResponse = data.get(dataName).getAsJsonObject(); + final int statusCode = messageResponse.get("statusCode").getAsInt(); + final String body = messageResponse.get("body").toString(); when(statusLine.getStatusCode()).thenReturn(statusCode); when(response.getEntity()).thenReturn(new StringEntity(body)); @@ -71,10 +69,10 @@ public void test_checkUserCredentials_success() throws IOException @Test public void test_checkUserCredentials_failOn400() throws IOException { - String dataName = "credentialFail400"; - JsonObject messageResponse = data.get(dataName).getAsJsonObject(); - int statusCode = messageResponse.get("statusCode").getAsInt(); - String body = messageResponse.get("body").toString(); + final String dataName = "credentialFail400"; + final JsonObject messageResponse = data.get(dataName).getAsJsonObject(); + final int statusCode = messageResponse.get("statusCode").getAsInt(); + final String body = messageResponse.get("body").toString(); when(statusLine.getStatusCode()).thenReturn(statusCode); when(response.getEntity()).thenReturn(new StringEntity(body)); @@ -85,10 +83,10 @@ public void test_checkUserCredentials_failOn400() throws IOException @Test public void test_checkUserCredentials_failOn401() throws IOException { - String dataName = "credentialFail401"; - JsonObject messageResponse = data.get(dataName).getAsJsonObject(); - int statusCode = messageResponse.get("statusCode").getAsInt(); - String body = messageResponse.get("body").toString(); + final String dataName = "credentialFail401"; + final JsonObject messageResponse = data.get(dataName).getAsJsonObject(); + final int statusCode = messageResponse.get("statusCode").getAsInt(); + final String body = messageResponse.get("body").toString(); when(statusLine.getStatusCode()).thenReturn(statusCode); when(response.getEntity()).thenReturn(new StringEntity(body)); @@ -99,10 +97,10 @@ public void test_checkUserCredentials_failOn401() throws IOException @Test public void test_checkUserCredentials_failOn429() throws IOException { - String dataName = "credentialFail429"; - JsonObject messageResponse = data.get(dataName).getAsJsonObject(); - int statusCode = messageResponse.get("statusCode").getAsInt(); - String body = messageResponse.get("body").toString(); + final String dataName = "credentialFail429"; + final JsonObject messageResponse = data.get(dataName).getAsJsonObject(); + final int statusCode = messageResponse.get("statusCode").getAsInt(); + final String body = messageResponse.get("body").toString(); when(statusLine.getStatusCode()).thenReturn(statusCode); when(response.getEntity()).thenReturn(new StringEntity(body)); @@ -113,10 +111,10 @@ public void test_checkUserCredentials_failOn429() throws IOException @Test public void test_checkUserCredentials_failOn500() throws IOException { - String dataName = "credentialFail500"; - JsonObject messageResponse = data.get(dataName).getAsJsonObject(); - int statusCode = messageResponse.get("statusCode").getAsInt(); - String body = messageResponse.get("body").toString(); + final String dataName = "credentialFail500"; + final JsonObject messageResponse = data.get(dataName).getAsJsonObject(); + final int statusCode = messageResponse.get("statusCode").getAsInt(); + final String body = messageResponse.get("body").toString(); when(statusLine.getStatusCode()).thenReturn(statusCode); when(response.getEntity()).thenReturn(new StringEntity(body)); @@ -124,125 +122,58 @@ public void test_checkUserCredentials_failOn500() throws IOException assertThrows("Could not authorize with your credential due to problems when contacting JIRA API.", ConfigException.class, () -> jiraClient.checkUserCredentials(task)); } - @Test - public void test_getTotalCount_success() throws IOException - { - String dataName = "totalCountSuccess"; - JsonObject messageResponse = data.get(dataName).getAsJsonObject(); - int statusCode = messageResponse.get("statusCode").getAsInt(); - String body = messageResponse.get("body").toString(); - - when(statusLine.getStatusCode()).thenReturn(statusCode); - when(response.getEntity()).thenReturn(new StringEntity(body)); - - int totalCount = jiraClient.getTotalCount(task); - assertEquals(totalCount, messageResponse.get("body").getAsJsonObject().get("total").getAsInt()); - } - - @Test - public void test_getTotalCount_failOnRetry() throws IOException - { - String dataName = "totalCountFailAllTime"; - JsonObject messageResponse = data.get(dataName).getAsJsonObject(); - int statusCode = messageResponse.get("statusCode").getAsInt(); - String body = messageResponse.get("body").toString(); - - when(statusLine.getStatusCode()).thenReturn(statusCode); - when(response.getEntity()).thenReturn(new StringEntity(body)); - - assertThrows(RuntimeException.class, () -> jiraClient.getTotalCount(task)); - - // First try + 3 retry_limit - int expectedInvocation = 3 + 1; - verify(jiraClient, times(expectedInvocation)).createHttpClient(); - verify(statusLine, times(expectedInvocation)).getStatusCode(); - } - - @Test - public void test_getTotalCount_doNotRetryOn400Status() throws IOException - { - String dataName = "totalCountFail400"; - JsonObject messageResponse = data.get(dataName).getAsJsonObject(); - int statusCode = messageResponse.get("statusCode").getAsInt(); - String body = messageResponse.get("body").toString(); - - when(statusLine.getStatusCode()).thenReturn(statusCode); - when(response.getEntity()).thenReturn(new StringEntity(body)); - - assertThrows(RuntimeException.class, () -> jiraClient.getTotalCount(task)); - - // No retry - int expectedInvocation = 1; - verify(jiraClient, times(expectedInvocation)).createHttpClient(); - verify(statusLine, times(expectedInvocation)).getStatusCode(); - } - - @Test - public void test_getTotalCount_retryOnIOException() throws IOException - { - when(client.execute(Mockito.any())).thenThrow(new IOException("test exeception")); - - assertThrows(RuntimeException.class, () -> jiraClient.getTotalCount(task)); - - // First try + 3 retry_limit - int expectedInvocation = 3 + 1; - verify(jiraClient, times(expectedInvocation)).createHttpClient(); - // getStatusCode is not triggered - verify(statusLine, times(0)).getStatusCode(); - } - @Test public void test_searchIssues() throws IOException { - String dataName = "searchIssuesSuccess"; - JsonObject messageResponse = data.get(dataName).getAsJsonObject(); + final String dataName = "searchIssuesSuccess"; + final JsonObject messageResponse = data.get(dataName).getAsJsonObject(); - int statusCode = messageResponse.get("statusCode").getAsInt(); - String body = messageResponse.get("body").toString(); + final int statusCode = messageResponse.get("statusCode").getAsInt(); + final String body = messageResponse.get("body").toString(); when(statusLine.getStatusCode()).thenReturn(statusCode); when(response.getEntity()).thenReturn(new StringEntity(body)); - List issues = jiraClient.searchIssues(task, 0, 50); + final List issues = JiraUtil.fromSearchResult(jiraClient.searchIssues(task, null, 50)); assertEquals(issues.size(), 2); } @Test public void test_searchIssues_failJql() throws IOException { - String dataName = "searchIssuesFailJql"; - JsonObject messageResponse = data.get(dataName).getAsJsonObject(); + final String dataName = "searchIssuesFailJql"; + final JsonObject messageResponse = data.get(dataName).getAsJsonObject(); - int statusCode = messageResponse.get("statusCode").getAsInt(); - String body = messageResponse.get("body").toString(); + final int statusCode = messageResponse.get("statusCode").getAsInt(); + final String body = messageResponse.get("body").toString(); when(statusLine.getStatusCode()).thenReturn(statusCode); when(response.getEntity()).thenReturn(new StringEntity(body)); - assertThrows(ConfigException.class, () -> jiraClient.searchIssues(task, 0, 50)); + assertThrows(ConfigException.class, () -> jiraClient.searchIssues(task, null, 50)); } @Test public void test_searchIssues_emptyJql() throws IOException { - String dataName = "searchIssuesSuccess"; - JsonObject messageResponse = data.get(dataName).getAsJsonObject(); + final String dataName = "searchIssuesSuccess"; + final JsonObject messageResponse = data.get(dataName).getAsJsonObject(); - int statusCode = messageResponse.get("statusCode").getAsInt(); - String body = messageResponse.get("body").toString(); + final int statusCode = messageResponse.get("statusCode").getAsInt(); + final String body = messageResponse.get("body").toString(); when(statusLine.getStatusCode()).thenReturn(statusCode); when(response.getEntity()).thenReturn(new StringEntity(body)); ConfigSource config = TestHelpers.config().remove("jql"); task = CONFIG_MAPPER.map(config, PluginTask.class); - List issues = jiraClient.searchIssues(task, 0, 50); + List issues = JiraUtil.fromSearchResult(jiraClient.searchIssues(task, null, 50)); assertEquals(issues.size(), 2); config = TestHelpers.config().set("jql", ""); task = CONFIG_MAPPER.map(config, PluginTask.class); - issues = jiraClient.searchIssues(task, 0, 50); + issues = JiraUtil.fromSearchResult(jiraClient.searchIssues(task, null, 50)); assertEquals(issues.size(), 2); } } diff --git a/src/test/java/org/embulk/input/jira/util/JiraUtilTest.java b/src/test/java/org/embulk/input/jira/util/JiraUtilTest.java index 460aa80..c21c480 100644 --- a/src/test/java/org/embulk/input/jira/util/JiraUtilTest.java +++ b/src/test/java/org/embulk/input/jira/util/JiraUtilTest.java @@ -54,7 +54,7 @@ public static void setUp() throws IOException @Test public void test_calculateTotalPage() { - int resultPerPage = 50; + final int resultPerPage = 50; int expected = 0; int totalCount = 0; int actual = JiraUtil.calculateTotalPage(totalCount, resultPerPage); @@ -103,27 +103,27 @@ public void test_buildPermissionUrl() @Test public void test_buildSearchUrl() throws IOException { - PluginTask task = CONFIG_MAPPER.map(TestHelpers.config(), PluginTask.class); - String expected = "https://example.com/rest/api/latest/search"; - String actual = JiraUtil.buildSearchUrl(task.getUri()); + final PluginTask task = CONFIG_MAPPER.map(TestHelpers.config(), PluginTask.class); + final String expected = "https://example.com/rest/api/latest/search/jql"; + final String actual = JiraUtil.buildSearchUrl(task.getUri()); assertEquals(expected, actual); } @Test public void test_validateTaskConfig_allValid() throws IOException { - ConfigSource configSource = TestHelpers.config(); - PluginTask task = CONFIG_MAPPER.map(configSource, PluginTask.class); + final ConfigSource configSource = TestHelpers.config(); + final PluginTask task = CONFIG_MAPPER.map(configSource, PluginTask.class); JiraUtil.validateTaskConfig(task); } @Test public void test_validateTaskConfig_emptyUsername() throws IOException { - ConfigException exception = assertThrows("Username or email could not be empty", ConfigException.class, () -> { - ConfigSource configSource = TestHelpers.config(); + final ConfigException exception = assertThrows("Username or email could not be empty", ConfigException.class, () -> { + final ConfigSource configSource = TestHelpers.config(); configSource.set("username", ""); - PluginTask task = CONFIG_MAPPER.map(configSource, PluginTask.class); + final PluginTask task = CONFIG_MAPPER.map(configSource, PluginTask.class); JiraUtil.validateTaskConfig(task); }); assertEquals("Username or email could not be empty", exception.getMessage()); @@ -132,10 +132,10 @@ public void test_validateTaskConfig_emptyUsername() throws IOException @Test public void test_validateTaskConfig_emptyPassword() throws IOException { - ConfigException exception = assertThrows(ConfigException.class, () -> { - ConfigSource configSource = TestHelpers.config(); + final ConfigException exception = assertThrows(ConfigException.class, () -> { + final ConfigSource configSource = TestHelpers.config(); configSource.set("password", ""); - PluginTask task = CONFIG_MAPPER.map(configSource, PluginTask.class); + final PluginTask task = CONFIG_MAPPER.map(configSource, PluginTask.class); JiraUtil.validateTaskConfig(task); }); assertEquals("Password could not be empty", exception.getMessage()); @@ -144,10 +144,10 @@ public void test_validateTaskConfig_emptyPassword() throws IOException @Test public void test_validateTaskConfig_emptyUri() throws IOException { - ConfigException exception = assertThrows(ConfigException.class, () -> { - ConfigSource configSource = TestHelpers.config(); + final ConfigException exception = assertThrows(ConfigException.class, () -> { + final ConfigSource configSource = TestHelpers.config(); configSource.set("uri", ""); - PluginTask task = CONFIG_MAPPER.map(configSource, PluginTask.class); + final PluginTask task = CONFIG_MAPPER.map(configSource, PluginTask.class); JiraUtil.validateTaskConfig(task); }); assertEquals("JIRA API endpoint could not be empty", exception.getMessage()); @@ -156,100 +156,100 @@ public void test_validateTaskConfig_emptyUri() throws IOException @Test public void test_validateTaskConfig_nonExistedUri() throws IOException { - ConfigException exception = assertThrows(ConfigException.class, () -> { - ConfigSource configSource = TestHelpers.config(); + final ConfigException exception = assertThrows(ConfigException.class, () -> { + final ConfigSource configSource = TestHelpers.config(); configSource.set("uri", "https://not-existed-domain"); - PluginTask task = CONFIG_MAPPER.map(configSource, PluginTask.class); + final PluginTask task = CONFIG_MAPPER.map(configSource, PluginTask.class); JiraUtil.validateTaskConfig(task); }); assertEquals("JIRA API endpoint is incorrect or not available", exception.getMessage()); } @Test - public void test_validateTaskConfig_invalidUriProtocol() throws IOException + public void test_validateTaskConfig_invalidUriProtocol() { - ConfigException exception = assertThrows(ConfigException.class, () -> { - ConfigSource configSource = TestHelpers.config(); + final ConfigException exception = assertThrows(ConfigException.class, () -> { + final ConfigSource configSource = TestHelpers.config(); configSource.set("uri", "ftp://example.com"); - PluginTask task = CONFIG_MAPPER.map(configSource, PluginTask.class); + final PluginTask task = CONFIG_MAPPER.map(configSource, PluginTask.class); JiraUtil.validateTaskConfig(task); }); assertEquals("JIRA API endpoint is incorrect or not available", exception.getMessage()); } @Test - public void test_validateTaskConfig_containSpaceUri() throws IOException + public void test_validateTaskConfig_containSpaceUri() { - ConfigException exception = assertThrows(ConfigException.class, () -> { - ConfigSource configSource = TestHelpers.config(); + final ConfigException exception = assertThrows(ConfigException.class, () -> { + final ConfigSource configSource = TestHelpers.config(); configSource.set("uri", "https://example .com"); - PluginTask task = CONFIG_MAPPER.map(configSource, PluginTask.class); + final PluginTask task = CONFIG_MAPPER.map(configSource, PluginTask.class); JiraUtil.validateTaskConfig(task); }); assertEquals("JIRA API endpoint is incorrect or not available", exception.getMessage()); } @Test - public void test_validateTaskConfig_emptyJql() throws IOException + public void test_validateTaskConfig_emptyJql() { - ConfigSource configSource = TestHelpers.config(); + final ConfigSource configSource = TestHelpers.config(); configSource.set("jql", ""); - PluginTask task = CONFIG_MAPPER.map(configSource, PluginTask.class); + final PluginTask task = CONFIG_MAPPER.map(configSource, PluginTask.class); JiraUtil.validateTaskConfig(task); } @Test - public void test_validateTaskConfig_missingJql() throws IOException + public void test_validateTaskConfig_missingJql() { - ConfigSource configSource = TestHelpers.config(); + final ConfigSource configSource = TestHelpers.config(); configSource.remove("jql"); - PluginTask task = CONFIG_MAPPER.map(configSource, PluginTask.class); + final PluginTask task = CONFIG_MAPPER.map(configSource, PluginTask.class); JiraUtil.validateTaskConfig(task); } @Test - public void test_validateTaskConfig_RetryIntervalIs0() throws IOException + public void test_validateTaskConfig_RetryIntervalIs0() { - ConfigException exception = assertThrows(ConfigException.class, () -> { - ConfigSource configSource = TestHelpers.config(); + final ConfigException exception = assertThrows(ConfigException.class, () -> { + final ConfigSource configSource = TestHelpers.config(); configSource.set("initial_retry_interval_millis", 0); - PluginTask task = CONFIG_MAPPER.map(configSource, PluginTask.class); + final PluginTask task = CONFIG_MAPPER.map(configSource, PluginTask.class); JiraUtil.validateTaskConfig(task); }); assertEquals("Initial retry delay should be equal or greater than 1", exception.getMessage()); } @Test - public void test_validateTaskConfig_RetryIntervalIsNegative() throws IOException + public void test_validateTaskConfig_RetryIntervalIsNegative() { - ConfigException exception = assertThrows(ConfigException.class, () -> { - ConfigSource configSource = TestHelpers.config(); + final ConfigException exception = assertThrows(ConfigException.class, () -> { + final ConfigSource configSource = TestHelpers.config(); configSource.set("initial_retry_interval_millis", -1); - PluginTask task = CONFIG_MAPPER.map(configSource, PluginTask.class); + final PluginTask task = CONFIG_MAPPER.map(configSource, PluginTask.class); JiraUtil.validateTaskConfig(task); }); assertEquals("Initial retry delay should be equal or greater than 1", exception.getMessage()); } @Test - public void test_validateTaskConfig_RetryLimitGreaterThan10() throws IOException + public void test_validateTaskConfig_RetryLimitGreaterThan10() { - ConfigException exception = assertThrows(ConfigException.class, () -> { - ConfigSource configSource = TestHelpers.config(); + final ConfigException exception = assertThrows(ConfigException.class, () -> { + final ConfigSource configSource = TestHelpers.config(); configSource.set("retry_limit", 11); - PluginTask task = CONFIG_MAPPER.map(configSource, PluginTask.class); + final PluginTask task = CONFIG_MAPPER.map(configSource, PluginTask.class); JiraUtil.validateTaskConfig(task); }); assertEquals("Retry limit should between 0 and 10", exception.getMessage()); } @Test - public void test_validateTaskConfig_RetryLimitLessThan0() throws IOException + public void test_validateTaskConfig_RetryLimitLessThan0() { - ConfigException exception = assertThrows(ConfigException.class, () -> { - ConfigSource configSource = TestHelpers.config(); + final ConfigException exception = assertThrows(ConfigException.class, () -> { + final ConfigSource configSource = TestHelpers.config(); configSource.set("retry_limit", -1); - PluginTask task = CONFIG_MAPPER.map(configSource, PluginTask.class); + final PluginTask task = CONFIG_MAPPER.map(configSource, PluginTask.class); JiraUtil.validateTaskConfig(task); }); assertEquals("Retry limit should between 0 and 10", exception.getMessage()); @@ -258,19 +258,19 @@ public void test_validateTaskConfig_RetryLimitLessThan0() throws IOException @Test public void test_addRecord_allRight() { - String testName = "allRight"; - Issue issue = new Issue(data.get(testName).getAsJsonObject()); - PageBuilder mock = Mockito.mock(PageBuilder.class); - - Boolean boolValue = Boolean.TRUE; - Long longValue = Long.valueOf(1); - Double doubleValue = Double.valueOf(1); - String stringValue = "string"; - Instant dateValue = TimestampFormatter + final String testName = "allRight"; + final Issue issue = new Issue(data.get(testName).getAsJsonObject()); + final PageBuilder mock = Mockito.mock(PageBuilder.class); + + final Boolean boolValue = Boolean.TRUE; + final Long longValue = Long.valueOf(1); + final Double doubleValue = Double.valueOf(1); + final String stringValue = "string"; + final Instant dateValue = TimestampFormatter .builder("%Y-%m-%dT%H:%M:%S.%L%z", true) .setDefaultZoneFromString("UTC") .build().parse("2019-01-01T00:00:00.000Z"); - Value jsonValue = new JsonParser().parse("{}"); + final Value jsonValue = new JsonParser().parse("{}"); JiraUtil.addRecord(issue, schema, pluginTask, mock); @@ -285,12 +285,12 @@ public void test_addRecord_allRight() @Test public void test_addRecord_allWrong() { - String testName = "allWrong"; - Issue issue = new Issue(data.get(testName).getAsJsonObject()); - PageBuilder mock = Mockito.mock(PageBuilder.class); + final String testName = "allWrong"; + final Issue issue = new Issue(data.get(testName).getAsJsonObject()); + final PageBuilder mock = Mockito.mock(PageBuilder.class); - String stringValue = "{}"; - Value jsonValue = new JsonParser().parse("{}"); + final String stringValue = "{}"; + final Value jsonValue = new JsonParser().parse("{}"); JiraUtil.addRecord(issue, schema, pluginTask, mock); @@ -305,9 +305,9 @@ public void test_addRecord_allWrong() @Test public void test_addRecord_allMissing() { - String testName = "allMissing"; - Issue issue = new Issue(data.get(testName).getAsJsonObject()); - PageBuilder mock = Mockito.mock(PageBuilder.class); + final String testName = "allMissing"; + final Issue issue = new Issue(data.get(testName).getAsJsonObject()); + final PageBuilder mock = Mockito.mock(PageBuilder.class); JiraUtil.addRecord(issue, schema, pluginTask, mock); @@ -317,11 +317,11 @@ public void test_addRecord_allMissing() @Test public void test_addRecord_arrayAsString() { - String testName = "arrayAsString"; - Issue issue = new Issue(data.get(testName).getAsJsonObject()); - PageBuilder mock = Mockito.mock(PageBuilder.class); + final String testName = "arrayAsString"; + final Issue issue = new Issue(data.get(testName).getAsJsonObject()); + final PageBuilder mock = Mockito.mock(PageBuilder.class); - String stringValue = "1,{},[]"; + final String stringValue = "1,{},[]"; JiraUtil.addRecord(issue, schema, pluginTask, mock); diff --git a/src/test/resources/jira_input_plugin.json b/src/test/resources/jira_input_plugin.json index 1fe9868..176e2ef 100644 --- a/src/test/resources/jira_input_plugin.json +++ b/src/test/resources/jira_input_plugin.json @@ -10,18 +10,14 @@ "emptyResult": { "statusCode": 200, "body": { - "startAt": 0, "maxResults": 50, - "total": 0, "issues": [] } }, "oneRecordResult": { "statusCode": 200, "body": { - "startAt": 0, "maxResults": 50, - "total": 1, "issues": [ { "id": "id1", @@ -42,9 +38,7 @@ "guessDataResult": { "statusCode": 200, "body": { - "startAt": 0, "maxResults": 50, - "total": 1, "issues": [ { "fields": { @@ -91,9 +85,8 @@ "2PagesResult": { "statusCode": 200, "body": { - "startAt": 0, "maxResults": 50, - "total": 51, + "nextPageToken": "nextPageToken", "issues": [ { "id": "id1",