Skip to content

Commit

Permalink
feat: support token filter with translated value
Browse files Browse the repository at this point in the history
  • Loading branch information
vietnguyen committed Nov 17, 2023
1 parent 77a9662 commit 8327350
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,20 @@
*/
package org.hisp.dhis.query.operators;

import static org.hisp.dhis.user.UserSettingKey.DB_LOCALE;
import static org.hisp.dhis.user.UserSettingKey.UI_LOCALE;

import java.util.Locale;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import org.apache.commons.lang3.ObjectUtils;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Restrictions;
import org.hisp.dhis.hibernate.jsonb.type.JsonbFunctions;
import org.hisp.dhis.query.Typed;
import org.hisp.dhis.query.planner.QueryPath;
import org.hisp.dhis.user.CurrentUserUtil;

/**
* @author Henning Håkonsen
Expand Down Expand Up @@ -62,11 +68,29 @@ public Criterion getHibernateCriterion(QueryPath queryPath) {
public <Y> Predicate getPredicate(CriteriaBuilder builder, Root<Y> root, QueryPath queryPath) {
String value = caseSensitive ? getValue(String.class) : getValue(String.class).toLowerCase();

Predicate defaultSearch =
builder.equal(
builder.function(
JsonbFunctions.REGEXP_SEARCH,
Boolean.class,
root.get(queryPath.getPath()),
builder.literal(TokenUtils.createRegex(value).toString())),
true);
Locale currentLocale =
ObjectUtils.defaultIfNull(
CurrentUserUtil.getUserSetting(DB_LOCALE), CurrentUserUtil.getUserSetting(UI_LOCALE));
if (currentLocale == null
|| !queryPath.getProperty().isTranslatable()
|| queryPath.getProperty().getTranslationKey() == null) {
return defaultSearch;
}
return builder.equal(
builder.function(
JsonbFunctions.REGEXP_SEARCH,
JsonbFunctions.SEARCH_TRANSLATION_TOKEN,
Boolean.class,
root.get(queryPath.getPath()),
root.get("translations"),
builder.literal("{" + queryPath.getProperty().getTranslationKey() + "}"),
builder.literal(currentLocale.getLanguage()),
builder.literal(TokenUtils.createRegex(value).toString())),
true);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
Find translated value that matches given token and given locale.
@param $1 the translations column name
@param $2 the properties to search for (array of strings such as '{NAME,SHORT_NAME}')
@param $3 the locale language to search for
@param $4 the token to search (example : '(?=.*année)')
*/
CREATE OR replace FUNCTION jsonb_search_translated_token(jsonb, text, text, text)
RETURNS bool
AS $$
SELECT exists(
SELECT 1
FROM jsonb_array_elements($1) trans
WHERE trans->>'property' = ANY ($2::text[])
AND trans->>'locale' = $3
AND trans->>'value' ~* $4
);
$$
LANGUAGE SQL IMMUTABLE PARALLEL SAFE;
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,6 @@ public class JsonbFunctions {
* to search $2 Regular expression for matching
*/
public static final String REGEXP_SEARCH = "regexp_search";

public static final String SEARCH_TRANSLATION_TOKEN = "jsonb_search_translated_token";
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,23 @@

import static org.hisp.dhis.utils.JavaToJson.toJson;
import static org.hisp.dhis.web.WebClientUtils.assertStatus;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.hisp.dhis.jsontree.JsonArray;
import org.hisp.dhis.user.User;
import org.hisp.dhis.user.UserSettingKey;
import org.hisp.dhis.user.UserSettingService;
import org.hisp.dhis.utils.JavaToJson;
import org.hisp.dhis.web.HttpStatus;
import org.hisp.dhis.webapi.DhisControllerConvenienceTest;
import org.hisp.dhis.webapi.DhisControllerIntegrationTest;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;

/**
* Base class for testing the {@link DatastoreController} providing helpers to set up entries in the
Expand Down Expand Up @@ -68,4 +79,52 @@ final void postPet(String key, String name, int age, List<String> eats) {
eats == null ? List.of() : eats.stream().map(food -> Map.of("name", food)));
postEntry("pets", key, toJson(obj));
}

static class CrudControllerIntegrationTest extends DhisControllerIntegrationTest {

@Autowired private UserSettingService userSettingService;

@Test
void testSearchByToken() {
String id =
assertStatus(
HttpStatus.CREATED,
POST(
"/dataSets/",
"{'name':'My data set', 'shortName': 'MDS', 'periodType':'Monthly'}"));

PUT(
"/dataSets/" + id + "/translations",
"{'translations': [{'locale':'fr', 'property':'NAME', 'value':'fr dataSet'}]}")
.content(HttpStatus.NO_CONTENT);

JsonArray translations =
GET("/dataSets/{id}/translations", id).content().getArray("translations");
assertEquals(1, translations.size());

assertTrue(
GET("/dataSets?filter=identifiable:token:fr", id)
.content()
.getArray("dataSets")
.isEmpty());
User userA = createAndAddUser("userA", null, "ALL");
userSettingService.saveUserSetting(UserSettingKey.DB_LOCALE, Locale.FRENCH, userA);
injectSecurityContext(userA);
assertTrue(
GET("/dataSets?filter=identifiable:token:bb", id)
.content()
.getArray("dataSets")
.isEmpty());
assertFalse(
GET("/dataSets?filter=identifiable:token:fr", id)
.content()
.getArray("dataSets")
.isEmpty());
assertFalse(
GET("/dataSets?filter=identifiable:token:dataSet", id)
.content()
.getArray("dataSets")
.isEmpty());
}
}
}

0 comments on commit 8327350

Please sign in to comment.