Skip to content

Commit

Permalink
Fix #10198: Filter tags and classifications if the classification is …
Browse files Browse the repository at this point in the history
…disabled (#12144)

* Fix #10198: Filter tags and classifications if the classification is disabled

* Fix #10198: Fix tests

* Fix #10198: Fix tests

* Fix #10198: Fix Postgres tests

* Fix #10198: Fix Postgres tests

* Fix #12083 - Backoff retry when polling for pipeline service client status (#12105)

* Fix #12083

* Fix #12083

---------

Co-authored-by: Sriharsha Chintalapani <harshach@users.noreply.github.com>

* Databricks Support Table Constraints (#12138)

* Databricks Support Table Constraints

* pylint fix

---------

Co-authored-by: Pere Miquel Brull <peremiquelbrull@gmail.com>

* Fix #11659: Add support for filter patterns in dbt workflow (#12063)

* Move Top Level Imports (#12145)

* interfaces for db migrations (#12057)

* interfaces for db migrations

* Add Data Migration for entities to 1.1

* Migration for FQN hash

* Added Tag Usage And Fixed some transaction issue in testing

* Added SERVER_CHANGE_LOG validations

* Update Path for files

* Added Postgres Migration

* Test Suite Migration [WIP]

* remove ingestion pipeline

* moved Migration to Table Initializer

* init Data source config in TableInitializer

* make checksum ignore configurable

---------

Co-authored-by: mohitdeuex <mohit.y@deuexsolutions.com>

* Fix Migrations (#12146)

* Mask queries, Table sample data and Topic sample data (#12139)

* Filter out PII queries

* Mask topic PII

* Mask topic PII

* Update sample data test

* Format

* Moved logic to repository or PIIMasker

* Fix test

* chore(ui): update sample data api endpoint for topic

---------

Co-authored-by: Sachin Chaurasiya <sachinchaurasiyachotey87@gmail.com>

* Dockerfile Update (#11066)

* Updating Dockerfile with multistage

* Updating reviewed changes

* Docker Development changes

* Docker workflow changes

* Arguments update

* Script path update

* Arguments update

* Resolving the reviewed suggestions

* Docker script update (#12115)

* Create manual.yml

* Fixing Typo

* Removing manual test github action file

* Testing IP runner

* Updating docker script for sandbox db task

* removing the ip test action

* Updating docker script for sandbox db task postgres

* Volume cleaning argument addition

* Volume cleaning argument addition

* Fix Failing Maven CI Test (#12151)

* Fix SQLLineage Test (#12152)

* Docker server workflow (#12157)

* Updating Dockerfile with multistage

* Updating reviewed changes

* Docker Development changes

* Docker workflow changes

* Arguments update

* Script path update

* Arguments update

* Resolving the reviewed suggestions

* updating workflow syntax

* remvoed ibm db arch (#12158)

* fix: resolve general feedbacks (#12153)

* fix: feedbacks

* fix: add tier as static item

* fix: feedbacks

* fix: add localization

* fix: feedbacks

* fix: add localization

* fix: breadcrumb wrapping issue

* fix: alignment issue for glossary

* fix: border issue in drawer

* ui: worked on DQ feedback part 3 (#12149)

* ui: worked on DQ feedback part 3

* updated name of test suite ingestion name
- Added description field in test case expand view
- Added functionality to update displayname of test case

* added testCase in re-indexing

* integrated permission in DQ

* fix: check on undefined bool value of clause (#12162)

* chore(ui): task feedback (#12154)

* chore(ui): task feedback

* fix copy notification info

* fix code font

* fix glue pipeline

* fix unit tests

* fix cy failures

* Update CODEOWNERS for UI code

* Address backend DQ feedback (#12150)

* fix: add jp and zh index

* fix: updated testCase tooltips

* fix: changed list testSuite permission to VEW_TEST + set owner of table for executable testSuite

* Run System Migrations on CREATE

* Update logging messages for JWT tokens (#12169)

* Update Test Case Activity Feed Messaging

* Auto tagging Windows troubleshooting (#12173)

* feat(ui): supported pagination and search in tags and term select for both table and entity level (#12155)

* supported pagination and search in tags and term select for both table and entity level

* supported pagination of tags in glossary overiew section for tags

* fix cypress and address comments

* fix cypress issue

* fixed(ui): updated tour flow and feedback (#12171)

* fixed(ui): updated tour flow and feedback

* translation-sync

* addressing comments

* feat: added view definition tab in table details page (#12172)

* Update profiler dq doc (#12182)

* doc: update documentation for profiler and dq

* doc: renamed image path from 1.0.0 to 1.1.0

* fix connection kwargs error in profiler (#12181)

* Fix Profiler Workflow infinite loop (#12185)

* Fix Profiler Workflow infinite loop

* fix: scrolling behavior feedback on mydata page (#12176)

* fix(ui): task layout update (#12175)

* fix(ui): task layout update

* fix title for edit task

* remove local for edit-task

* feat(ui): supported pagination of glossary terms in glossary overview section (#12177)

* supported pagination of glossary terms in glossay overview section

* remove unwanted try catch

* change as per comments

* Update S3 storage permissions (#12064)

* Add disabled flag to search mappings

* Revert unwanted changes

---------

Co-authored-by: Pere Miquel Brull <peremiquelbrull@gmail.com>
Co-authored-by: Mayur Singal <39544459+ulixius9@users.noreply.github.com>
Co-authored-by: Ayush Shah <ayush@getcollate.io>
Co-authored-by: mohitdeuex <mohit.y@deuexsolutions.com>
Co-authored-by: Sachin Chaurasiya <sachinchaurasiyachotey87@gmail.com>
Co-authored-by: Anuj359 <anuj.j@deuexsolutions.com>
Co-authored-by: Mohit Yadav <105265192+mohityadav766@users.noreply.github.com>
Co-authored-by: Onkar Ravgan <onkar.10r@gmail.com>
Co-authored-by: karanh37 <33024356+karanh37@users.noreply.github.com>
Co-authored-by: Shailesh Parmar <shailesh.parmar.webdev@gmail.com>
Co-authored-by: Teddy <teddy.crepineau@gmail.com>
Co-authored-by: Chirag Madlani <12962843+chirag-madlani@users.noreply.github.com>
Co-authored-by: Ashish Gupta <ashish@getcollate.io>
  • Loading branch information
14 people authored Jun 28, 2023
1 parent dc76546 commit 4744084
Show file tree
Hide file tree
Showing 11 changed files with 349 additions and 66 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,29 @@
import java.util.Map;
import org.openmetadata.schema.entity.classification.Tag;
import org.openmetadata.service.Entity;
import org.openmetadata.service.elasticsearch.ElasticSearchIndexUtils;
import org.openmetadata.service.elasticsearch.models.ElasticSearchSuggest;
import org.openmetadata.service.util.JsonUtils;

public class TagIndex implements ElasticSearchIndex {
final Tag tag;
private static final List<String> excludeFields = List.of("changeDescription");

public TagIndex(Tag tag) {
this.tag = tag;
}

public Map<String, Object> buildESDoc() {
Map<String, Object> doc = JsonUtils.getMap(tag);
ElasticSearchIndexUtils.removeNonIndexableFields(doc, excludeFields);
List<ElasticSearchSuggest> suggest = new ArrayList<>();
suggest.add(ElasticSearchSuggest.builder().input(tag.getFullyQualifiedName()).weight(5).build());
suggest.add(ElasticSearchSuggest.builder().input(tag.getName()).weight(10).build());
if (tag.getDisabled() != null && tag.getDisabled()) {
doc.put("disabled", tag.getDisabled());
} else {
doc.put("disabled", "false");
}
doc.put("suggest", suggest);
doc.put("entityType", Entity.TAG);
return doc;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1810,6 +1810,119 @@ default String getNameHashColumn() {

@SqlUpdate("DELETE FROM tag where fqnHash LIKE CONCAT(:fqnHashPrefix, '.%')")
void deleteTagsByPrefix(@Bind("fqnHashPrefix") String fqnHashPrefix);

@Override
default int listCount(ListFilter filter) {
boolean disabled = Boolean.parseBoolean(filter.getQueryParam("classification.disabled"));
String condition =
String.format(
"INNER JOIN entity_relationship er ON tag.id=er.toId AND er.relation=%s AND er.fromEntity='%s' "
+ "INNER JOIN classification c on er.fromId=c.id",
CONTAINS.ordinal(), Entity.CLASSIFICATION);
String mySqlCondition = condition;
String postgresCondition = condition;

if (disabled) {
mySqlCondition =
String.format(
"%s AND (JSON_EXTRACT(c.json, '$.disabled') IS NULL OR JSON_EXTRACT(c.json, '$.disabled') = TRUE)",
mySqlCondition);
postgresCondition =
String.format(
"%s AND ((c.json#>'{disabled}') IS NULL OR ((c.json#>'{disabled}')::boolean) = TRUE)",
postgresCondition);
} else {
mySqlCondition =
String.format(
"%s AND (JSON_EXTRACT(c.json, '$.disabled') IS NULL OR JSON_EXTRACT(c.json, '$.disabled') = FALSE)",
mySqlCondition);
postgresCondition =
String.format(
"%s AND ((c.json#>'{disabled}') IS NULL OR ((c.json#>'{disabled}')::boolean) = FALSE)",
postgresCondition);
}

mySqlCondition = String.format("%s %s", mySqlCondition, filter.getCondition("tag"));
postgresCondition = String.format("%s %s", postgresCondition, filter.getCondition("tag"));
return listCount(getTableName(), getNameColumn(), mySqlCondition, postgresCondition);
}

@Override
default List<String> listBefore(ListFilter filter, int limit, String before) {
boolean disabled = Boolean.parseBoolean(filter.getQueryParam("classification.disabled"));
String condition =
String.format(
"INNER JOIN entity_relationship er ON tag.id=er.toId AND er.relation=%s AND er.fromEntity='%s' "
+ "INNER JOIN classification c on er.fromId=c.id",
CONTAINS.ordinal(), Entity.CLASSIFICATION);

String mySqlCondition = condition;
String postgresCondition = condition;

if (disabled) {
mySqlCondition =
String.format(
"%s AND (JSON_EXTRACT(c.json, '$.disabled') IS NULL OR JSON_EXTRACT(c.json, '$.disabled') = TRUE)",
mySqlCondition);
postgresCondition =
String.format(
"%s AND ((c.json#>'{disabled}') IS NULL OR ((c.json#>'{disabled}')::boolean) = TRUE)",
postgresCondition);
} else {
mySqlCondition =
String.format(
"%s AND (JSON_EXTRACT(c.json, '$.disabled') IS NULL OR JSON_EXTRACT(c.json, '$.disabled') = FALSE)",
mySqlCondition);
postgresCondition =
String.format(
"%s AND ((c.json#>'{disabled}') IS NULL OR ((c.json#>'{disabled}')::boolean) = FALSE)",
postgresCondition);
}

mySqlCondition = String.format("%s %s", mySqlCondition, filter.getCondition("tag"));
postgresCondition = String.format("%s %s", postgresCondition, filter.getCondition("tag"));

return listBefore(getTableName(), getNameColumn(), mySqlCondition, postgresCondition, limit, before);
}

@Override
default List<String> listAfter(ListFilter filter, int limit, String after) {
String parent = filter.getQueryParam("parent");

boolean disabled = Boolean.parseBoolean(filter.getQueryParam("classification.disabled"));
String condition =
String.format(
"INNER JOIN entity_relationship er ON tag.id=er.toId AND er.relation=%s AND er.fromEntity='%s' "
+ "INNER JOIN classification c on er.fromId=c.id",
CONTAINS.ordinal(), Entity.CLASSIFICATION);

String mySqlCondition = condition;
String postgresCondition = condition;

if (disabled) {
mySqlCondition =
String.format(
"%s AND (JSON_EXTRACT(c.json, '$.disabled') IS NULL OR JSON_EXTRACT(c.json, '$.disabled') = TRUE)",
mySqlCondition);
postgresCondition =
String.format(
"%s AND ((c.json#>'{disabled}') IS NULL OR ((c.json#>'{disabled}')::boolean) = TRUE)",
postgresCondition);
} else {
mySqlCondition =
String.format(
"%s AND (JSON_EXTRACT(c.json, '$.disabled') IS NULL OR JSON_EXTRACT(c.json, '$.disabled') = FALSE)",
mySqlCondition);
postgresCondition =
String.format(
"%s AND ((c.json#>'{disabled}') IS NULL OR ((c.json#>'{disabled}')::boolean) = FALSE)",
postgresCondition);
}

mySqlCondition = String.format("%s %s", mySqlCondition, filter.getCondition("tag"));
postgresCondition = String.format("%s %s", postgresCondition, filter.getCondition("tag"));
return listAfter(getTableName(), getNameColumn(), mySqlCondition, postgresCondition, limit, after);
}
}

@RegisterRowMapper(TagLabelMapper.class)
Expand Down Expand Up @@ -2114,66 +2227,6 @@ default List<String> listAfter(ListFilter filter, int limit, String after) {
return listAfter(getTableName(), getNameColumn(), mySqlCondition, postgresCondition, limit, after);
}

@ConnectionAwareSqlQuery(value = "SELECT count(*) FROM <table> <mysqlCond>", connectionType = MYSQL)
@ConnectionAwareSqlQuery(value = "SELECT count(*) FROM <table> <postgresCond>", connectionType = POSTGRES)
int listCount(
@Define("table") String table,
@Define("nameColumn") String nameColumn,
@Define("mysqlCond") String mysqlCond,
@Define("postgresCond") String postgresCond);

@ConnectionAwareSqlQuery(
value =
"SELECT json FROM ("
+ "SELECT <nameColumn>, json FROM <table> <mysqlCond> AND "
+ "<nameColumn> < :before "
+ // Pagination by entity fullyQualifiedName or name (when entity does not have fqn)
"ORDER BY <nameColumn> DESC "
+ // Pagination ordering by entity fullyQualifiedName or name (when entity does not have fqn)
"LIMIT :limit"
+ ") last_rows_subquery ORDER BY <nameColumn>",
connectionType = MYSQL)
@ConnectionAwareSqlQuery(
value =
"SELECT json FROM ("
+ "SELECT <nameColumn>, json FROM <table> <postgresCond> AND "
+ "<nameColumn> < :before "
+ // Pagination by entity fullyQualifiedName or name (when entity does not have fqn)
"ORDER BY <nameColumn> DESC "
+ // Pagination ordering by entity fullyQualifiedName or name (when entity does not have fqn)
"LIMIT :limit"
+ ") last_rows_subquery ORDER BY <nameColumn>",
connectionType = POSTGRES)
List<String> listBefore(
@Define("table") String table,
@Define("nameColumn") String nameColumn,
@Define("mysqlCond") String mysqlCond,
@Define("postgresCond") String postgresCond,
@Bind("limit") int limit,
@Bind("before") String before);

@ConnectionAwareSqlQuery(
value =
"SELECT json FROM <table> <mysqlCond> AND "
+ "<nameColumn> > :after "
+ "ORDER BY <nameColumn> "
+ "LIMIT :limit",
connectionType = MYSQL)
@ConnectionAwareSqlQuery(
value =
"SELECT json FROM <table> <postgresCond> AND "
+ "<nameColumn> > :after "
+ "ORDER BY <nameColumn> "
+ "LIMIT :limit",
connectionType = POSTGRES)
List<String> listAfter(
@Define("table") String table,
@Define("nameColumn") String nameColumn,
@Define("mysqlCond") String mysqlCond,
@Define("postgresCond") String postgresCond,
@Bind("limit") int limit,
@Bind("after") String after);

default List<String> listTeamsUnderOrganization(String teamId) {
return listTeamsUnderOrganization(teamId, Relationship.PARENT_OF.ordinal());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import org.openmetadata.service.Entity;
import org.openmetadata.service.exception.CatalogExceptionMessage;
import org.openmetadata.service.exception.EntityNotFoundException;
import org.openmetadata.service.jdbi3.locator.ConnectionAwareSqlQuery;
import org.openmetadata.service.jdbi3.locator.ConnectionAwareSqlUpdate;
import org.openmetadata.service.util.FullyQualifiedName;
import org.openmetadata.service.util.JsonUtils;
Expand Down Expand Up @@ -135,6 +136,66 @@ String findByName(
@SqlQuery("SELECT count(*) FROM <table> <cond>")
int listCount(@Define("table") String table, @Define("nameColumn") String nameColumn, @Define("cond") String cond);

@ConnectionAwareSqlQuery(value = "SELECT count(*) FROM <table> <mysqlCond>", connectionType = MYSQL)
@ConnectionAwareSqlQuery(value = "SELECT count(*) FROM <table> <postgresCond>", connectionType = POSTGRES)
int listCount(
@Define("table") String table,
@Define("nameColumn") String nameColumn,
@Define("mysqlCond") String mysqlCond,
@Define("postgresCond") String postgresCond);

@ConnectionAwareSqlQuery(
value =
"SELECT json FROM ("
+ "SELECT <table>.<nameColumn>, <table>.json FROM <table> <mysqlCond> AND "
+ "<table>.<nameColumn> < :before "
+ // Pagination by entity fullyQualifiedName or name (when entity does not have fqn)
"ORDER BY <table>.<nameColumn> DESC "
+ // Pagination ordering by entity fullyQualifiedName or name (when entity does not have fqn)
"LIMIT :limit"
+ ") last_rows_subquery ORDER BY <nameColumn>",
connectionType = MYSQL)
@ConnectionAwareSqlQuery(
value =
"SELECT json FROM ("
+ "SELECT <table>.<nameColumn>, <table>.json FROM <table> <postgresCond> AND "
+ "<table>.<nameColumn> < :before "
+ // Pagination by entity fullyQualifiedName or name (when entity does not have fqn)
"ORDER BY <table>.<nameColumn> DESC "
+ // Pagination ordering by entity fullyQualifiedName or name (when entity does not have fqn)
"LIMIT :limit"
+ ") last_rows_subquery ORDER BY <nameColumn>",
connectionType = POSTGRES)
List<String> listBefore(
@Define("table") String table,
@Define("nameColumn") String nameColumn,
@Define("mysqlCond") String mysqlCond,
@Define("postgresCond") String postgresCond,
@Bind("limit") int limit,
@Bind("before") String before);

@ConnectionAwareSqlQuery(
value =
"SELECT <table>.json FROM <table> <mysqlCond> AND "
+ "<table>.<nameColumn> > :after "
+ "ORDER BY <table>.<nameColumn> "
+ "LIMIT :limit",
connectionType = MYSQL)
@ConnectionAwareSqlQuery(
value =
"SELECT <table>.json FROM <table> <postgresCond> AND "
+ "<table>.<nameColumn> > :after "
+ "ORDER BY <table>.<nameColumn> "
+ "LIMIT :limit",
connectionType = POSTGRES)
List<String> listAfter(
@Define("table") String table,
@Define("nameColumn") String nameColumn,
@Define("mysqlCond") String mysqlCond,
@Define("postgresCond") String postgresCond,
@Bind("limit") int limit,
@Bind("after") String after);

@SqlQuery("SELECT count(*) FROM <table>")
int listTotalCount(@Define("table") String table, @Define("nameColumn") String nameColumn);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public String getCondition(String tableName) {
condition = addCondition(condition, getServiceCondition(tableName));
condition = addCondition(condition, getPipelineTypeCondition(tableName));
condition = addCondition(condition, getParentCondition(tableName));
condition = addCondition(condition, getDisabledCondition(tableName));
condition = addCondition(condition, getCategoryCondition(tableName));
condition = addCondition(condition, getWebhookCondition(tableName));
condition = addCondition(condition, getWebhookTypeCondition(tableName));
Expand Down Expand Up @@ -88,6 +89,30 @@ public String getParentCondition(String tableName) {
return parentFqn == null ? "" : getFqnPrefixCondition(tableName, parentFqn);
}

public String getDisabledCondition(String tableName) {
String disabled = queryParams.get("disabled");
return disabled == null ? "" : getDisabledCondition(tableName, disabled);
}

public String getDisabledCondition(String tableName, String disabledStr) {
boolean disabled = Boolean.parseBoolean(disabledStr);
String disabledCondition = "";
if (DatasourceConfig.getInstance().isMySQL()) {
if (disabled) {
disabledCondition = "JSON_EXTRACT(json, '$.disabled') = TRUE";
} else {
disabledCondition = "(JSON_EXTRACT(json, '$.disabled') IS NULL OR JSON_EXTRACT(json, '$.disabled') = FALSE)";
}
} else {
if (disabled) {
disabledCondition = "((c.json#>'{disabled}')::boolean) = TRUE)";
} else {
disabledCondition = "(c.json#>'{disabled}' IS NULL OR ((c.json#>'{disabled}'):boolean) = FALSE";
}
}
return disabledCondition;
}

public String getCategoryCondition(String tableName) {
String category = queryParams.get("category");
return category == null ? "" : getCategoryPrefixCondition(tableName, category);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ public ResultList<Classification> list(
schema = @Schema(type = "string", example = FIELDS))
@QueryParam("fields")
String fieldsParam,
@Parameter(description = "Filter Disabled Classifications") @QueryParam("disabled") String disabled,
@Parameter(description = "Limit the number classifications returned. (1 to 1000000, default = " + "10) ")
@DefaultValue("10")
@Min(0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,10 @@ public ResultList<Tag> list(
schema = @Schema(type = "string", example = FIELDS))
@QueryParam("fields")
String fieldsParam,
@Parameter(description = "Filter Disabled Classifications", schema = @Schema(type = "string", example = FIELDS))
@QueryParam("disabled")
@DefaultValue("false")
Boolean disabled,
@Parameter(description = "Limit the number tags returned. (1 to 1000000, " + "default = 10)")
@DefaultValue("10")
@Min(0)
Expand All @@ -245,7 +249,8 @@ public ResultList<Tag> list(
@DefaultValue("non-deleted")
Include include)
throws IOException {
ListFilter filter = new ListFilter(include).addQueryParam("parent", parent);
ListFilter filter =
new ListFilter(include).addQueryParam("parent", parent).addQueryParam("classification.disabled", disabled);
return super.listInternal(uriInfo, securityContext, fieldsParam, filter, limitParam, before, after);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -642,7 +642,8 @@ private static SearchSourceBuilder buildTagSearchBuilder(String query, int from,
QueryBuilders.queryStringQuery(query)
.field(FIELD_NAME, 10.0f)
.field(EntityBuilderConstant.FIELD_DISPLAY_NAME, 10.0f)
.field(EntityBuilderConstant.FIELD_DISPLAY_NAME_NGRAM, 1.0f)
.field(EntityBuilderConstant.FIELD_NAME_NGRAM, 1.0f)
.field("classification.name", 1.0f)
.field(EntityBuilderConstant.DESCRIPTION, 3.0f)
.defaultOperator(Operator.AND)
.fuzziness(Fuzziness.AUTO);
Expand Down Expand Up @@ -1153,13 +1154,20 @@ public void updateDashboardService(ChangeEvent event) throws IOException {

@Override
public void updateClassification(ChangeEvent event) throws IOException {
Classification classification = (Classification) event.getEntity();
String indexName = ElasticSearchIndexDefinition.ElasticSearchIndexType.TAG_SEARCH_INDEX.indexName;
if (event.getEventType() == ENTITY_DELETED) {
Classification classification = (Classification) event.getEntity();
DeleteByQueryRequest request =
new DeleteByQueryRequest(ElasticSearchIndexDefinition.ElasticSearchIndexType.TAG_SEARCH_INDEX.indexName);
DeleteByQueryRequest request = new DeleteByQueryRequest(indexName);
String fqnMatch = classification.getName() + ".*";
request.setQuery(new WildcardQueryBuilder("fullyQualifiedName", fqnMatch));
deleteEntityFromElasticSearchByQuery(request);
} else if (event.getEventType() == ENTITY_UPDATED) {
UpdateByQueryRequest updateByQueryRequest = new UpdateByQueryRequest(indexName);
updateByQueryRequest.setQuery(new MatchQueryBuilder("tag.classification.id", classification.getId().toString()));
String scriptTxt = "ctx._source.disabled=true";
Script script = new Script(ScriptType.INLINE, Script.DEFAULT_SCRIPT_LANG, scriptTxt, new HashMap<>());
updateByQueryRequest.setScript(script);
updateElasticSearchByQuery(updateByQueryRequest);
}
}

Expand Down
Loading

0 comments on commit 4744084

Please sign in to comment.