Skip to content
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

fix: Use org unit stored path [DHIS2-18234] #19626

Open
wants to merge 38 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
9f6aa32
fix: Update code
larshelge Jan 8, 2025
0a9c389
fix: Update code
larshelge Jan 8, 2025
cd080b8
fix: Update code
larshelge Jan 8, 2025
4a34535
fix: Update code
larshelge Jan 8, 2025
34adbec
fix: Update code
larshelge Jan 8, 2025
a2032c3
fix: Update code
larshelge Jan 8, 2025
63fe07b
fix: Update code
larshelge Jan 8, 2025
e6023f8
fix: Update code
larshelge Jan 8, 2025
9aee496
fix: Update code
larshelge Jan 9, 2025
6440d25
fix: Update code
larshelge Jan 9, 2025
b4e3f4c
fix: Update code
larshelge Jan 9, 2025
edc6b99
fix: Update code
larshelge Jan 9, 2025
3131421
fix: Update code
larshelge Jan 9, 2025
a80a3b7
fix: Update code
larshelge Jan 9, 2025
c5d74af
fix: Update code
larshelge Jan 9, 2025
2d47e18
fix: Update code
larshelge Jan 9, 2025
3e41a1c
fix: Update code
larshelge Jan 9, 2025
5b1362c
fix: Update code
larshelge Jan 9, 2025
c10dfda
fix: Update code
larshelge Jan 9, 2025
01b2f41
fix: Update code
larshelge Jan 9, 2025
f85ad5b
Merge branch 'org-unit-unit-test' into org-unit-persisted-path
larshelge Jan 9, 2025
a7a5630
fix: Update code
larshelge Jan 9, 2025
4901058
fix: Update code
larshelge Jan 9, 2025
e3f5b78
fix: Update code
larshelge Jan 9, 2025
de5c1a0
Merge branch 'org-unit-unit-test' into org-unit-persisted-path
larshelge Jan 9, 2025
5f4d477
Merge branch 'master' into org-unit-persisted-path
larshelge Jan 9, 2025
703ef31
fix: Update code
larshelge Jan 9, 2025
22a2220
fix: Update code
larshelge Jan 9, 2025
66da90a
fix: Update code
larshelge Jan 9, 2025
461ae15
fix: Update code
larshelge Jan 9, 2025
cbedfb1
fix: Update code
larshelge Jan 9, 2025
249cd4c
fix: Update code
larshelge Jan 9, 2025
cc9ec97
fix: Update code
larshelge Jan 9, 2025
770ff5c
fix: Update code
larshelge Jan 9, 2025
048a51c
fix: Update code
larshelge Jan 9, 2025
9e832e5
fix: Update code
larshelge Jan 9, 2025
a480b83
fix: Update code
larshelge Jan 9, 2025
9b27a3f
fix: Update code
larshelge Jan 9, 2025
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 @@ -95,7 +95,7 @@ public DeflatedDataValue(DataValue dataValue) {
this.dataElementId = dataValue.getDataElement().getId();
this.periodId = dataValue.getPeriod().getId();
this.sourceId = dataValue.getSource().getId();
this.sourcePath = dataValue.getSource().getPath();
this.sourcePath = dataValue.getSource().getStoredPath();
this.categoryOptionComboId = dataValue.getCategoryOptionCombo().getId();
this.attributeOptionComboId = dataValue.getAttributeOptionCombo().getId();
this.value = dataValue.getValue();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@
package org.hisp.dhis.organisationunit;

import static org.apache.commons.collections4.CollectionUtils.isEmpty;
import static org.apache.commons.lang3.StringUtils.isNotEmpty;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
Expand Down Expand Up @@ -574,7 +576,7 @@ public boolean isDescendant(Collection<OrganisationUnit> ancestors) {
return ancestors.stream()
.filter(Objects::nonNull)
.map(OrganisationUnit::getUid)
.anyMatch(uid -> StringUtils.contains(this.getPath(), uid));
.anyMatch(uid -> StringUtils.contains(this.getStoredPath(), uid));
}

/**
Expand All @@ -588,7 +590,7 @@ public boolean isDescendant(OrganisationUnit ancestor) {
return false;
}

return StringUtils.contains(this.getPath(), ancestor.getUid());
return StringUtils.contains(this.getStoredPath(), ancestor.getUid());
}

public Set<OrganisationUnit> getChildrenThisIfEmpty() {
Expand Down Expand Up @@ -754,6 +756,13 @@ public void setChildren(Set<OrganisationUnit> children) {
this.children = children;
}

/**
* Note that the {@code path} property is mapped with the "property access" mode. This method will
* calculate and return the path property value based on the org unit ancestors. To access the
* {@code path} property directly, use {@link OrganisationUnit#getStoredPath}.
*
* @return the path.
*/
@JsonProperty
@JacksonXmlProperty(namespace = DxfNamespaces.DXF_2_0)
public String getPath() {
Expand All @@ -780,11 +789,36 @@ public String getPath() {
return this.path;
}

/** Do not set directly, managed by persistence layer. */
/**
* Note that the {@code path} property is mapped with the "property access" mode. This method will
* return the persisted {@code path} property value directly. If the path is not defined,
* typically as part of an integration test where the state is not yet flushed to the database,
* the calculated path based on the org unit ancestors is returned. To get the calculated path
* value explicitly, use {@link OrganisationUnit#getPath}.
*
* @return the path.
*/
@JsonIgnore
public String getStoredPath() {
return isNotEmpty(path) ? path : getPath();
}

/**
* Note that the {@code path} property is mapped with the "property access" mode. Do not set
* directly, this property is managed by the persistence layer.
*/
public void setPath(String path) {
this.path = path;
}

/**
* Note that the {@code path} property is mapped with the "property access" mode. This method is
* for unit testing purposes only.
*/
public void updatePath() {
setPath(getPath());
}

/**
* Used by persistence layer. Purpose is to have a column for use in database queries. For
* application use see {@link OrganisationUnit#getLevel()} which has better performance.
Expand All @@ -807,14 +841,6 @@ public Integer getHierarchyLevel() {
return hierarchyLevel;
}

/**
* Note that the {@code path} is mapped with the "property access" mode. This method is for unit
* testing purposes only.
*/
public void updatePath() {
setPath(getPath());
}

/** Do not set directly. */
public void setHierarchyLevel(Integer hierarchyLevel) {
this.hierarchyLevel = hierarchyLevel;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ private String getSelectStatement(DataQueryParams params, List<DimensionalObject
for (DimensionalItemObject item : dim.getItems()) {
OrganisationUnit unit = (OrganisationUnit) item;

sql += DIM_NAME_OU + " like '" + unit.getPath() + "%' or ";
sql += DIM_NAME_OU + " like '" + unit.getStoredPath() + "%' or ";
}

sql = TextUtils.removeLastOr(sql) + ") ";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,10 @@ public static String getOrgUnitPathClause(
StringBuilder sql = new StringBuilder(relation + " (");
orgUnits.forEach(
ou ->
sql.append(pathAlias).append(".\"path\" like '").append(ou.getPath()).append("%' or "));
sql.append(pathAlias)
.append(".\"path\" like '")
.append(ou.getStoredPath())
.append("%' or "));

return StringUtils.trim(TextUtils.removeLastOr(sql.toString())) + ")";
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ List<Object[]> createBatchObjects(List<OrganisationUnit> units, int level) {
values.add(unit.getOpeningDate());
values.add(unit.getClosedDate());
values.add(level);
values.add(unit.getPath());
values.add(unit.getStoredPath());

Map<Integer, Long> identifiers = new HashMap<>();
Map<Integer, String> uids = new HashMap<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ private Set<String> getUserOrgUnitPaths() {
User currentUser = userService.getUserByUsername(CurrentUserUtil.getCurrentUsername());
Set<String> allUserOrgUnitPaths =
currentUser.getOrganisationUnits().stream()
.map(OrganisationUnit::getPath)
.map(OrganisationUnit::getStoredPath)
.collect(Collectors.toSet());

return allUserOrgUnitPaths.stream()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ private String getDataValuesHql(

hql.append(
params.getOrganisationUnits().stream()
.map(OrganisationUnit::getPath)
.map(OrganisationUnit::getStoredPath)
.map(p -> "ou.path like '" + p + "%'")
.collect(joining(" or ")));

Expand Down Expand Up @@ -565,7 +565,7 @@ private void getDdvOrgUnits(
where
.append(sqlHelper.or())
.append("ou.path like '")
.append(parent.getPath())
.append(parent.getStoredPath())
.append("%'");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public Object evaluate(ExpressionParser.ExprContext ctx, CommonExpressionVisitor

if (orgUnit != null) {
for (TerminalNode uid : ctx.UID()) {
if (orgUnit.getPath().contains(uid.getText() + "/")) {
if (orgUnit.getStoredPath().contains(uid.getText() + "/")) {
return true;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ public Long getOrganisationUnitHierarchyMemberCount(
+ ")";

Query<Long> query = getTypedQuery(hql);
query.setParameter("path", parent.getPath() + "%").setParameter("object", member);
query.setParameter("path", parent.getStoredPath() + "%").setParameter("object", member);

return query.getSingleResult();
}
Expand Down Expand Up @@ -244,7 +244,7 @@ public List<OrganisationUnit> getOrganisationUnits(OrganisationUnitQueryParams p

if (params.hasParents()) {
for (OrganisationUnit parent : params.getParents()) {
query.setParameter(parent.getUid(), parent.getPath() + "%");
query.setParameter(parent.getUid(), parent.getStoredPath() + "%");
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ private static String createOrgUnitClause(

clause +=
params.getOrganisationUnits().stream()
.map(o -> " ou.path LIKE '" + o.getPath() + "%' OR ")
.map(o -> " ou.path LIKE '" + o.getStoredPath() + "%' OR ")
.collect(Collectors.joining());

return TextUtils.removeLastOr(clause) + " ) ";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ private String getDataValueSql(DataExportParams params) {
sql += "and (";

for (OrganisationUnit parent : params.getOrganisationUnits()) {
sql += "ou.path like '" + parent.getPath() + "%' or ";
sql += "ou.path like '" + parent.getStoredPath() + "%' or ";
}

sql = TextUtils.removeLastOr(sql) + ") ";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -883,7 +883,7 @@ private DataValueContextBuilder createDataValueContext(DataValue dataValue) {
ou.setUid(ouId);
// we set the path here just for the tests. This is usually done by the persistence layer
// but there is no interaction with that in these tests.
ou.setPath(ou.getPath());
ou.updatePath();
builder.orgUnit(ou);
}
if (coId != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,8 @@ public void init(
this.outputDataElementOperand = outputDataElementOperand;

orgUnitLookup =
orgUnits.stream().collect(Collectors.toMap(OrganisationUnit::getPath, Function.identity()));
orgUnits.stream()
.collect(Collectors.toMap(OrganisationUnit::getStoredPath, Function.identity()));
dataElementLookup =
dataElements.stream()
.collect(toMap(DataElement::getId, Function.identity(), (de1, de2) -> de1));
Expand Down Expand Up @@ -359,7 +360,7 @@ private PredictionData getPredictionData(
addValueToMap(dv, map);
}

if (ddv.getSourcePath().equals(dv.getSource().getPath())
if (ddv.getSourcePath().equals(dv.getSource().getStoredPath())
&& ddv.getDataElementId() == outputDataElementOperand.getDataElement().getId()
&& (outputDataElementOperand.getCategoryOptionCombo() == null
|| ddv.getCategoryOptionComboId()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ public List<String> canCreate(
Program program = enrollment.getProgram();
List<String> errors = new ArrayList<>();
OrganisationUnit ou = enrollment.getOrganisationUnit();
if (ou != null && !user.isInUserHierarchy(ou.getPath())) {
if (ou != null && !user.isInUserHierarchy(ou.getStoredPath())) {
errors.add("User has no create access to organisation unit: " + ou.getUid());
}

Expand Down Expand Up @@ -336,7 +336,7 @@ public List<String> canUpdate(

} else {
OrganisationUnit ou = enrollment.getOrganisationUnit();
if (ou != null && !user.isInUserHierarchy(ou.getPath())) {
if (ou != null && !user.isInUserHierarchy(ou.getStoredPath())) {
errors.add("User has no write access to organisation unit: " + ou.getUid());
}
}
Expand Down Expand Up @@ -371,7 +371,7 @@ public List<String> canDelete(
}
} else {
OrganisationUnit ou = enrollment.getOrganisationUnit();
if (ou != null && !user.isInUserHierarchy(ou.getPath())) {
if (ou != null && !user.isInUserHierarchy(ou.getStoredPath())) {
errors.add("User has no delete access to organisation unit: " + ou.getUid());
}
}
Expand Down Expand Up @@ -447,8 +447,8 @@ public List<String> canCreate(
if (ou != null) {
boolean isInHierarchy =
event.isCreatableInSearchScope()
? user.isInUserEffectiveSearchOrgUnitHierarchy(ou.getPath())
: user.isInUserHierarchy(ou.getPath());
? user.isInUserEffectiveSearchOrgUnitHierarchy(ou.getStoredPath())
: user.isInUserHierarchy(ou.getStoredPath());

if (!isInHierarchy) {
errors.add("User has no create access to organisation unit: " + ou.getUid());
Expand Down Expand Up @@ -493,7 +493,7 @@ public List<String> canUpdate(
canManageWithRegistration(errors, user, programStage, program);

OrganisationUnit ou = event.getOrganisationUnit();
if (ou != null && !user.isInUserEffectiveSearchOrgUnitHierarchy(ou.getPath())) {
if (ou != null && !user.isInUserEffectiveSearchOrgUnitHierarchy(ou.getStoredPath())) {
errors.add("User has no update access to organisation unit: " + ou.getUid());
}

Expand Down Expand Up @@ -528,7 +528,7 @@ public List<String> canDelete(
List<String> errors = new ArrayList<>();
if (program.isWithoutRegistration()) {
OrganisationUnit ou = event.getOrganisationUnit();
if (ou != null && !user.isInUserHierarchy(ou.getPath())) {
if (ou != null && !user.isInUserHierarchy(ou.getStoredPath())) {
errors.add("User has no delete access to organisation unit: " + ou.getUid());
}

Expand Down Expand Up @@ -748,10 +748,10 @@ public boolean canAccess(@Nonnull UserDetails user, Program program, Organisatio
}

if (program != null && (program.isClosed() || program.isProtected())) {
return user.isInUserHierarchy(orgUnit.getPath());
return user.isInUserHierarchy(orgUnit.getStoredPath());
}

return user.isInUserEffectiveSearchOrgUnitHierarchy(orgUnit.getPath());
return user.isInUserEffectiveSearchOrgUnitHierarchy(orgUnit.getStoredPath());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ public boolean hasAccess(UserDetails user, TrackedEntity trackedEntity, Program

OrganisationUnit ou = getOwner(trackedEntity, program, trackedEntity::getOrganisationUnit);

final String orgUnitPath = ou.getPath();
final String orgUnitPath = ou.getStoredPath();
return switch (program.getAccessLevel()) {
case OPEN, AUDITED -> user.isInUserEffectiveSearchOrgUnitHierarchy(orgUnitPath);
case PROTECTED ->
Expand All @@ -243,7 +243,7 @@ public boolean hasAccess(
return true;
}

final String orgUnitPath = owningOrgUnit.getPath();
final String orgUnitPath = owningOrgUnit.getStoredPath();
return switch (program.getAccessLevel()) {
case OPEN, AUDITED -> user.isInUserEffectiveSearchOrgUnitHierarchy(orgUnitPath);
case PROTECTED ->
Expand All @@ -267,7 +267,7 @@ public boolean canSkipOwnershipCheck(UserDetails user, ProgramType programType)
public boolean isOwnerInUserSearchScope(
UserDetails user, TrackedEntity trackedEntity, Program program) {
return user.isInUserSearchHierarchy(
getOwner(trackedEntity, program, trackedEntity::getOrganisationUnit).getPath());
getOwner(trackedEntity, program, trackedEntity::getOrganisationUnit).getStoredPath());
}

// -------------------------------------------------------------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,8 @@ public Set<OrganisationUnit> validateOrgUnits(Set<UID> orgUnitIds, UserDetails u
throw new BadRequestException("Organisation unit does not exist: " + orgUnitUid);
}

if (!user.isSuper() && !user.isInUserEffectiveSearchOrgUnitHierarchy(orgUnit.getPath())) {
if (!user.isSuper()
&& !user.isInUserEffectiveSearchOrgUnitHierarchy(orgUnit.getStoredPath())) {
throw new ForbiddenException(
"Organisation unit is not part of the search scope: " + orgUnit.getUid());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ private String getDescendantsQuery(Set<OrganisationUnit> organisationUnits) {
ouClause
.append(orHlp.or())
.append("en.organisationUnit.path LIKE '")
.append(organisationUnit.getPath())
.append(organisationUnit.getStoredPath())
.append("%'");
}

Expand All @@ -252,7 +252,7 @@ private String getChildrenQuery(SqlHelper hlp, Set<OrganisationUnit> organisatio
orgUnits
.append(hlp.or())
.append("en.organisationUnit.path LIKE '")
.append(organisationUnit.getPath())
.append(organisationUnit.getStoredPath())
.append("%'")
.append(" AND (en.organisationUnit.hierarchyLevel = ")
.append(organisationUnit.getHierarchyLevel())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ private OrganisationUnit validateRequestedOrgUnit(String orgUnitUid, UserDetails
"Organisation unit is specified but does not exist: " + orgUnitUid);
}

if (!user.isInUserEffectiveSearchOrgUnitHierarchy(orgUnit.getPath())) {
if (!user.isInUserEffectiveSearchOrgUnitHierarchy(orgUnit.getStoredPath())) {
throw new ForbiddenException(
"Organisation unit is not part of your search scope: " + orgUnit.getUid());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1195,7 +1195,7 @@ private String createAccessibleSql(

private String createDescendantsSql(
User user, EventQueryParams params, MapSqlParameterSource mapSqlParameterSource) {
mapSqlParameterSource.addValue(COLUMN_ORG_UNIT_PATH, params.getOrgUnit().getPath());
mapSqlParameterSource.addValue(COLUMN_ORG_UNIT_PATH, params.getOrgUnit().getStoredPath());

if (isProgramRestricted(params.getProgram())) {
return createCaptureScopeQuery(
Expand All @@ -1208,7 +1208,7 @@ private String createDescendantsSql(

private String createChildrenSql(
User user, EventQueryParams params, MapSqlParameterSource mapSqlParameterSource) {
mapSqlParameterSource.addValue(COLUMN_ORG_UNIT_PATH, params.getOrgUnit().getPath());
mapSqlParameterSource.addValue(COLUMN_ORG_UNIT_PATH, params.getOrgUnit().getStoredPath());

String customChildrenQuery =
" and (ou.hierarchylevel = "
Expand All @@ -1231,7 +1231,7 @@ private String createChildrenSql(

private String createSelectedSql(
User user, EventQueryParams params, MapSqlParameterSource mapSqlParameterSource) {
mapSqlParameterSource.addValue(COLUMN_ORG_UNIT_PATH, params.getOrgUnit().getPath());
mapSqlParameterSource.addValue(COLUMN_ORG_UNIT_PATH, params.getOrgUnit().getStoredPath());

String orgUnitPathEqualsMatchQuery =
" ou.path = :"
Expand Down
Loading
Loading