Skip to content

Fix IncrementAnIndexPaginationIterator #61

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
* to contain above placeholder.
*/
public class IncrementAnIndexPaginationIterator extends BaseHttpPaginationIterator {
private static final Logger LOG = LoggerFactory.getLogger(IncrementAnIndexPaginationIterator.class);
public static final String PAGINATION_INDEX_PLACEHOLDER_REGEX = "\\{pagination.index\\}";

private final Long indexIncrement;
Expand All @@ -46,22 +45,30 @@ public IncrementAnIndexPaginationIterator(BaseHttpSourceConfig config, Paginatio
this.index = config.getStartIndex() - this.indexIncrement;
}

this.nextPageUrl = getNextPageUrl();
this.nextPageUrl = getNextPageUrl(null);
}

private String getNextPageUrl() {
private String getNextPageUrl(BasePage page) {
index += indexIncrement;

if (maxIndex != null && index > maxIndex) {
return null;
if (maxIndex != null) {
// If the index is greater than max index, we stop the pagination
if (index > maxIndex) {
return null;
}
} else {
return config.getUrl().replaceAll(PAGINATION_INDEX_PLACEHOLDER_REGEX, index.toString());
// If the page received is empty, we stop the pagination
if (page != null && page.next().getRecord() == null) {
return null;
}
}

return config.getUrl().replaceAll(PAGINATION_INDEX_PLACEHOLDER_REGEX, index.toString());
}

@Override
protected String getNextPageUrl(HttpResponse response, BasePage page) {
return getNextPageUrl();
return getNextPageUrl(page);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,6 @@
import java.util.List;
import java.util.Map;

/**
* Returns elements from json one by one by given json path.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why was this removed?

*/
class JsonPage extends BasePage {
private final String insideElementJsonPathPart;
private final Iterator<JsonElement> iterator;
Expand All @@ -43,17 +40,19 @@ class JsonPage extends BasePage {
private final BaseHttpSourceConfig config;
private final List<String> optionalFields;

// TODO : handle case where the result json object is null
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what happens when result json object is null? are you planning to handle that case in another PR?

JsonPage(BaseHttpSourceConfig config, HttpResponse httpResponse) {
super(httpResponse);
this.config = config;
String body = httpResponse.getBody();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where is this used?

this.json = JSONUtil.toJsonElement(httpResponse.getBody());
this.schema = config.getSchema();
this.optionalFields = getOptionalFields();

JsonElement jsonElement = json;
if (json.isJsonObject()) {
JSONUtil.JsonQueryResponse queryResponse =
JSONUtil.getJsonElementByPath(json.getAsJsonObject(), config.getResultPath(), optionalFields);
JSONUtil.getJsonElementByPath(json.getAsJsonObject(), config.getResultPath(), optionalFields);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

revert indent change

this.insideElementJsonPathPart = queryResponse.getUnretrievedPath();
jsonElement = queryResponse.get();
} else {
Expand All @@ -62,11 +61,11 @@ class JsonPage extends BasePage {

if (jsonElement.isJsonArray()) {
this.iterator = jsonElement.getAsJsonArray().iterator();
} else if (jsonElement.isJsonObject()) {
} else if (jsonElement.isJsonObject() || jsonElement.isJsonNull()) {
this.iterator = Collections.singleton(jsonElement).iterator();
} else {
throw new IllegalArgumentException(String.format("Element found by '%s' json path is expected to be an object " +
"or an array. Primitive found", config.getResultPath()));
"or an array. Primitive found", config.getResultPath()));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

revert indent change

}

this.fieldsMapping = config.getFullFieldsMapping();
Expand All @@ -79,56 +78,56 @@ public boolean hasNext() {

/**
* Converts a next element from json into a json object which is defined by fieldsMapping.
*
* <p>
* Example next element:
* {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why was this changed?

* "id":"19124",
* "key":"NETTY-13",
* "fields":{
* "issuetype":{
* "self":"https://issues.cask.co/rest/api/2/issuetype/4",
* "name":"Improvement",
* "subtask":false
* },
* "fixVersions":[
*
* ],
* "description":"Test description for NETTY-13",
* "project":{
* "id":"10301",
* "key":"NETTY",
* "name":"Netty-HTTP",
* "projectCategory":{
* "id":"10002",
* "name":"Infrastructure"
* }
* }
* }
* }
*
* {
* "id":"19124",
* "key":"NETTY-13",
* "fields":{
* "issuetype":{
* "self":"https://issues.cask.co/rest/api/2/issuetype/4",
* "name":"Improvement",
* "subtask":false
* },
* "fixVersions":[
* <p>
* ],
* "description":"Test description for NETTY-13",
* "project":{
* "id":"10301",
* "key":"NETTY",
* "name":"Netty-HTTP",
* "projectCategory":{
* "id":"10002",
* "name":"Infrastructure"
* }
* }
* }
* }
* <p>
* The mapping is:
*
* <p>
* | Field Name | Field Path |
* | --------------- |:-----------------------------------------:|
* | type | /fields/issuetype/name |
* | description | /fields/description |
* | projectCategory | /fields/project/projectCategory/name |
* | isSubtask | /fields/issuetype/subtask |
* | fixVersions | /fields/fixVersions |
*
* <p>
* The result returned by function is:
*
* <p>
* {
* "key":"NETTY-13",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why was this changed?

* "type":"Improvement",
* "isSubtask":false,
* "description":"Test description for NETTY-13",
* "projectCategory":"Infrastructure",
* "fixVersions":[
*
* ]
* "key":"NETTY-13",
* "type":"Improvement",
* "isSubtask":false,
* "description":"Test description for NETTY-13",
* "projectCategory":"Infrastructure",
* "fixVersions":[
* <p>
* ]
* }
*
* <p>
* Note:
* This also supports "insideElementJsonPath". Example would be the following: if path is
* '/bookstore/items/bookPublisherDetails'. The array which elements are retrieved from is /bookstore/items
Expand All @@ -139,36 +138,47 @@ public boolean hasNext() {
*/
@Override
public PageEntry next() {
JsonObject currentJsonObject = iterator.next().getAsJsonObject();

JsonObject resultJson = new JsonObject();
int numPartiallyRetrieved = 0;
for (Map.Entry<String, String> entry : fieldsMapping.entrySet()) {
String schemaFieldName = entry.getKey();
String fieldPath = insideElementJsonPathPart + "/" + StringUtils.stripStart(entry.getValue(), "/");

JSONUtil.JsonQueryResponse queryResponse =
JSONUtil.getJsonElementByPath(currentJsonObject, fieldPath, optionalFields);

if (!queryResponse.isFullyRetrieved()) {
numPartiallyRetrieved++;
}

resultJson.add(schemaFieldName, queryResponse.get());
}

String jsonString = resultJson.toString();
try {
StructuredRecord record = StructuredRecordStringConverter.fromJsonString(jsonString, schema);
if (numPartiallyRetrieved > 0) {
InvalidEntry<StructuredRecord> error =
new InvalidEntry<>(1, "Couldn't find all required fields in the record", record);
return new PageEntry(error, config.getErrorHandling());
if (iterator.hasNext()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can avoid one level of indent if you return when there are no entries:

if (!iterartor.hasNext()) {
   return new PageEntry(null);
}
...

JsonElement jsonElement = iterator.next();
if (jsonElement.isJsonNull()) {
return new PageEntry(null);
} else {
JsonObject currentJsonObject = jsonElement.getAsJsonObject();

JsonObject resultJson = new JsonObject();
int numPartiallyRetrieved = 0;
for (Map.Entry<String, String> entry : fieldsMapping.entrySet()) {
String schemaFieldName = entry.getKey();
String fieldPath = insideElementJsonPathPart + "/" + StringUtils.stripStart(entry.getValue(), "/");

JSONUtil.JsonQueryResponse queryResponse =
JSONUtil.getJsonElementByPath(currentJsonObject, fieldPath, optionalFields);

if (!queryResponse.isFullyRetrieved()) {
numPartiallyRetrieved++;
}

if (!queryResponse.getRetrievedPath().equals("/")) {
resultJson.add(schemaFieldName, queryResponse.get());
}

}

String jsonString = resultJson.toString();
try {
StructuredRecord record = StructuredRecordStringConverter.fromJsonString(jsonString, schema);
if (numPartiallyRetrieved > 0) {
InvalidEntry<StructuredRecord> error =
new InvalidEntry<>(1, "Couldn't find all required fields in the record", record);
return new PageEntry(error, config.getErrorHandling());
}
return new PageEntry(record);
} catch (Throwable e) {
return new PageEntry(InvalidEntryCreator.buildStringError(jsonString, e), config.getErrorHandling());
}
}
return new PageEntry(record);
} catch (Throwable e) {
return new PageEntry(InvalidEntryCreator.buildStringError(jsonString, e), config.getErrorHandling());
}
return new PageEntry(null);
}

private List<String> getOptionalFields() {
Expand Down Expand Up @@ -196,7 +206,7 @@ private List<String> getOptionalFields() {
public String getPrimitiveByPath(String path) {
if (json.isJsonObject()) {
JSONUtil.JsonQueryResponse queryResponse = JSONUtil.getJsonElementByPath(json.getAsJsonObject(),
path, optionalFields);
path, optionalFields);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

revert indent change

if (queryResponse.isFullyRetrieved()) {
return queryResponse.getAsJsonPrimitive().getAsString();
}
Expand Down