Skip to content

Commit 75f9711

Browse files
committed
Merge branch 'develop' into fb_revertSampleNameCase
# Conflicts: # api/src/org/labkey/api/query/AbstractQueryUpdateService.java # experiment/src/client/test/integration/DataClassCrud.ispec.ts
2 parents ae98796 + 4efc813 commit 75f9711

12 files changed

Lines changed: 131 additions & 95 deletions

File tree

api/src/org/labkey/api/audit/AuditHandler.java

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import org.jetbrains.annotations.Nullable;
55
import org.labkey.api.data.ColumnInfo;
66
import org.labkey.api.data.Container;
7+
import org.labkey.api.data.MultiValuedForeignKey;
78
import org.labkey.api.data.TableInfo;
89
import org.labkey.api.dataiterator.DataIterator;
910
import org.labkey.api.dataiterator.ExistingRecordDataIterator;
@@ -64,16 +65,27 @@ static Pair<Map<String, Object>, Map<String, Object>> getOldAndNewRecordForMerge
6465
// and we won't convert sample type and data class names into lower case.
6566
for (Map.Entry<String, Object> entry : existingRow.entrySet())
6667
{
68+
boolean isMultiValued = false;
6769
String key = entry.getKey();
6870
// getDatasetRows() (at least) should return key==column.getName(), expect getColumn(name) to work
6971
ColumnInfo col = null==table ? null : table.getColumn(key);
70-
String nameFromAlias = null != col
71-
? col.getName()
72-
: columns.stream()
73-
.filter(column -> column.getAlias().getId().equalsIgnoreCase(key))
74-
.map((ColumnInfo::getName))
75-
.findFirst()
76-
.orElse(key);
72+
if (col != null && col.getFk() instanceof MultiValuedForeignKey)
73+
isMultiValued = true;
74+
75+
String nameFromAlias = key;
76+
if (null != col)
77+
nameFromAlias = col.getName();
78+
else
79+
{
80+
ColumnInfo aliasColumn = columns.stream().filter(c -> c.getAlias().getId().equalsIgnoreCase(key)).findFirst().orElse(null);
81+
82+
if (aliasColumn != null)
83+
{
84+
if (aliasColumn.getFk() != null && aliasColumn.getFk() instanceof MultiValuedForeignKey)
85+
isMultiValued = true;
86+
nameFromAlias = aliasColumn.getName();
87+
}
88+
}
7789
String lcName = nameFromAlias.toLowerCase();
7890
// Preserve casing of inputs so we can show the names properly
7991
boolean isExpInput = false;
@@ -131,8 +143,22 @@ else if (newValue instanceof Number && oldValue != null)
131143
}
132144
else if (!Objects.equals(oldValue, newValue) || isExtraAuditField)
133145
{
134-
originalRow.put(nameFromAlias, oldValue);
135-
modifiedRow.put(nameFromAlias, newValue);
146+
// If multivalued columns change, the value in this table will remain the key to the junction table
147+
// but at this point newValue will look like the newly chosen values not that key. So we skip
148+
// this in the diff unless the value changes from non-null to null or vice versa.
149+
if (isMultiValued)
150+
{
151+
if ((oldValue == null && newValue != null) || (newValue == null && oldValue != null))
152+
{
153+
originalRow.put(nameFromAlias, oldValue);
154+
modifiedRow.put(nameFromAlias, newValue);
155+
}
156+
}
157+
else
158+
{
159+
originalRow.put(nameFromAlias, oldValue);
160+
modifiedRow.put(nameFromAlias, newValue);
161+
}
136162
}
137163
}
138164
else if (isExtraAuditField)

api/src/org/labkey/api/data/FieldKeyRowMap.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
import java.util.Map;
2828
import java.util.Set;
2929

30-
class FieldKeyRowMap implements Map<FieldKey, Object>
30+
public class FieldKeyRowMap implements Map<FieldKey, Object>
3131
{
3232
private final Results _results;
3333

@@ -131,8 +131,6 @@ public static Map<String, Object> toNameMap(Map<FieldKey, Object> rowMap)
131131
{
132132
Map<String, Object> map = new CaseInsensitiveHashMap<>();
133133
rowMap.forEach((key, value) -> {
134-
if (key.getParent() != null)
135-
throw new IllegalArgumentException("Multi-part field key '" + key + "' cannot be used as key in string map since it may not be unique.");
136134
if (map.containsKey(key.getName()))
137135
throw new IllegalArgumentException("Duplicate key '" + key + "' found in fieldKey map.");
138136
map.put(key.getName(), value);

api/src/org/labkey/api/exp/PropertyColumn.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import org.labkey.api.exp.property.PropertyService;
3636
import org.labkey.api.query.FieldKey;
3737
import org.labkey.api.query.PdLookupForeignKey;
38+
import org.labkey.api.query.QueryService;
3839
import org.labkey.api.query.SchemaKey;
3940
import org.labkey.api.security.User;
4041
import org.labkey.api.study.assay.FileLinkDisplayColumn;
@@ -182,7 +183,12 @@ public static void copyAttributes(
182183
}
183184

184185
if (user != null && ((pd.getLookupSchema() != null && pd.getLookupQuery() != null) || pd.getConceptURI() != null))
185-
to.setFk(PdLookupForeignKey.create(to.getParentTable().getUserSchema(), user, container, pd, cf));
186+
{
187+
// Issue 52504: Use proper container filter for lookups
188+
var _cf = pd.isLookup() ? QueryService.get().getContainerFilterForLookups(container, user) : cf;
189+
190+
to.setFk(PdLookupForeignKey.create(to.getParentTable().getUserSchema(), user, container, pd, _cf));
191+
}
186192

187193
to.setDefaultValueType(pd.getDefaultValueTypeEnum());
188194
to.setConditionalFormats(PropertyService.get().getConditionalFormats(pd));

api/src/org/labkey/api/query/AbstractQueryUpdateService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ public Map<Integer, Map<String, Object>> getExistingRows(User user, Container co
202202
for (Map.Entry<Integer, Map<String, Object>> key : keys.entrySet())
203203
{
204204
Map<String, Object> row = getRow(user, container, key.getValue(), verifyNoCrossFolderData);
205-
if (row != null)
205+
if (row != null && !row.isEmpty())
206206
{
207207
result.put(key.getKey(), row);
208208
if (verifyNoCrossFolderData)

api/src/org/labkey/api/query/QuerySettings.java

Lines changed: 24 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.apache.commons.lang3.StringUtils;
2222
import org.apache.logging.log4j.LogManager;
2323
import org.jetbrains.annotations.NotNull;
24+
import org.jetbrains.annotations.Nullable;
2425
import org.labkey.api.collections.CaseInsensitiveHashMap;
2526
import org.labkey.api.data.Aggregate;
2627
import org.labkey.api.data.AnalyticsProviderItem;
@@ -52,8 +53,6 @@
5253
import java.util.List;
5354
import java.util.Map;
5455

55-
import static org.apache.commons.lang3.StringUtils.isNotBlank;
56-
5756
public class QuerySettings
5857
{
5958
public static final String URL_PARAMETER_PREFIX = "param.";
@@ -98,7 +97,6 @@ public class QuerySettings
9897

9998
private final Map<String, Object> _queryParameters = new CaseInsensitiveHashMap<>();
10099

101-
102100
protected QuerySettings(String dataRegionName)
103101
{
104102
_dataRegionName = dataRegionName;
@@ -118,7 +116,6 @@ public QuerySettings(ViewContext context, String dataRegionName)
118116
assert MemTracker.getInstance().put(this);
119117
}
120118

121-
122119
/**
123120
* Init the QuerySettings using all the request parameters, from context.getPropertyValues().
124121
* @see UserSchema#getSettings(org.labkey.api.view.ViewContext, String, String)
@@ -132,7 +129,6 @@ public QuerySettings(ViewContext context, String dataRegionName, String queryNam
132129
assert MemTracker.getInstance().put(this);
133130
}
134131

135-
136132
/**
137133
* @param params all parameters from URL or POST, including dataregion.filter parameters
138134
* @param dataRegionName prefix for filter params etc
@@ -146,7 +142,6 @@ public QuerySettings(PropertyValues params, String dataRegionName)
146142
assert MemTracker.getInstance().put(this);
147143
}
148144

149-
150145
protected PropertyValues getPropertyValues(ViewContext context)
151146
{
152147
PropertyValues pvs = context.getBindPropertyValues();
@@ -158,7 +153,6 @@ protected PropertyValues getPropertyValues(ViewContext context)
158153
return pvs;
159154
}
160155

161-
162156
/**
163157
* @param url parameters for filter/sort
164158
*/
@@ -167,7 +161,6 @@ public void setSortFilterURL(ActionURL url)
167161
setSortFilter(url.getPropertyValues());
168162
}
169163

170-
171164
public void setSortFilter(PropertyValues pvs)
172165
{
173166
_filterSort = pvs;
@@ -185,7 +178,7 @@ public void setSortFilter(PropertyValues pvs)
185178
}
186179
}
187180

188-
protected String _getParameter(String param)
181+
protected @Nullable String _getParameter(String param)
189182
{
190183
PropertyValue pv = _filterSort.getPropertyValue(param);
191184
if (pv == null)
@@ -198,18 +191,17 @@ protected String _getParameter(String param)
198191
Object[] a = (Object[])v;
199192
v = a.length == 0 ? null : a[0];
200193
}
201-
return v == null ? null : String.valueOf(v);
194+
return v == null ? null : StringUtils.trimToNull(String.valueOf(v));
202195
}
203196

204197
public void init(ViewContext context)
205198
{
206199
init(getPropertyValues(context));
207200
}
208201

209-
210202
/**
211203
* Initialize QuerySettings from the PropertyValues, binds all fields that are supported on the URL
212-
*. such as viewName. Use setSortFilter() to provide sort filter parameters w/o affecting the other
204+
* such as viewName. Use setSortFilter() to provide sort filter parameters w/o affecting the other
213205
* properties.
214206
*/
215207
public void init(PropertyValues pvs)
@@ -220,33 +212,35 @@ public void init(PropertyValues pvs)
220212
setAnalyticsProviders(pvs);
221213

222214
// Let URL parameter control which query we show, even if we don't show the Query drop-down menu to let the user choose
223-
String param = param(QueryParam.queryName);
224-
String queryName = StringUtils.trimToNull(_getParameter(param));
215+
String queryName = _getParameter(param(QueryParam.queryName));
225216
if (queryName != null)
226217
{
227218
setQueryName(queryName);
228219
}
229220

230221
if (getAllowChooseView())
231222
{
232-
String viewName = StringUtils.trimToNull(_getParameter(param(QueryParam.viewName)));
223+
String viewName = _getParameter(param(QueryParam.viewName));
233224
if (viewName != null)
234225
{
235226
setViewName(viewName);
236227
}
228+
237229
String ignoreFilter = _getParameter(param(QueryParam.ignoreFilter));
238-
try
230+
if (ignoreFilter != null)
239231
{
240-
if (isNotBlank(ignoreFilter))
241-
_ignoreUserFilter = (Boolean) ConvertUtils.convert(ignoreFilter, Boolean.class);
242-
}
243-
catch (ConversionException e)
244-
{
245-
throwParameterParseException(QueryParam.ignoreFilter);
232+
try
233+
{
234+
_ignoreViewFilter = (Boolean) ConvertUtils.convert(ignoreFilter, Boolean.class);
235+
}
236+
catch (ConversionException e)
237+
{
238+
throwParameterParseException(QueryParam.ignoreFilter);
239+
}
246240
}
247241

248242
String reportId = _getParameter(param(QueryParam.reportId));
249-
if (isNotBlank(reportId))
243+
if (reportId != null)
250244
{
251245
var identifier = ReportService.get().getReportIdentifier(reportId, null, null);
252246
if (null == identifier)
@@ -259,7 +253,7 @@ public void init(PropertyValues pvs)
259253
if (_showRows == ShowRows.PAGINATED)
260254
{
261255
String offsetParam = _getParameter(param(QueryParam.offset));
262-
if (isNotBlank(offsetParam))
256+
if (offsetParam != null)
263257
{
264258
try
265259
{
@@ -274,7 +268,7 @@ public void init(PropertyValues pvs)
274268
}
275269

276270
String maxRowsParam = _getParameter(param(QueryParam.maxRows));
277-
if (isNotBlank(maxRowsParam))
271+
if (maxRowsParam != null)
278272
{
279273
try
280274
{
@@ -295,7 +289,7 @@ public void init(PropertyValues pvs)
295289
}
296290

297291
String containerFilterNameParam = _getParameter(param(QueryParam.containerFilterName));
298-
if (isNotBlank(containerFilterNameParam))
292+
if (containerFilterNameParam != null)
299293
{
300294
// fail fast
301295
if (null == ContainerFilter.getType(containerFilterNameParam))
@@ -319,7 +313,7 @@ public void init(PropertyValues pvs)
319313
}
320314
}
321315

322-
String columns = StringUtils.trimToNull(_getParameter(param(QueryParam.columns)));
316+
String columns = _getParameter(param(QueryParam.columns));
323317
if (null != columns)
324318
{
325319
String[] colArray = columns.split(",");
@@ -333,7 +327,7 @@ public void init(PropertyValues pvs)
333327
}
334328
}
335329

336-
String extraColumns = StringUtils.trimToNull(_getParameter(param(QueryParam.extraColumns)));
330+
String extraColumns = _getParameter(param(QueryParam.extraColumns));
337331
if (null != extraColumns)
338332
{
339333
String[] colArray = extraColumns.split(",");
@@ -347,13 +341,13 @@ public void init(PropertyValues pvs)
347341
}
348342
}
349343

350-
String selectionKey = StringUtils.trimToNull(_getParameter(param(QueryParam.selectionKey)));
344+
String selectionKey = _getParameter(param(QueryParam.selectionKey));
351345
if (null != selectionKey)
352346
setSelectionKey(selectionKey);
353347

354348
_parseQueryParameters(_filterSort);
355349

356-
String allowHeaderLock = StringUtils.trimToNull(_getParameter(param(QueryParam.allowHeaderLock)));
350+
String allowHeaderLock = _getParameter(param(QueryParam.allowHeaderLock));
357351
if (null != allowHeaderLock)
358352
{
359353
try

experiment/src/client/test/integration/DataClassCrud.ispec.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -233,8 +233,8 @@ describe('Import with update / merge', () => {
233233
it ("Issue 52922: Blank sample id in the file are getting ignored in update from file", async () => {
234234
const BLANK_KEY_UPDATE_ERROR_NO_EXPRESSION = 'Missing value for required property: Name';
235235
const BLANK_KEY_UPDATE_ERROR_WITH_EXPRESSION = 'Name value not provided on row ';
236-
const BOGUS_KEY_UPDATE_ERROR = 'Data not found for ';
237-
const CROSS_FOLDER_UPDATE_NOT_SUPPORTED_ERROR = "Data doesn't belong to folder ";
236+
const BOGUS_KEY_UPDATE_ERROR = 'Data not found: ';
237+
const DUPLICATE_KEY_ERROR = 'duplicate key value';
238238

239239
const dataType = "NoExpressionNameRequired52922";
240240
const createPayload = {
@@ -306,9 +306,9 @@ describe('Import with update / merge', () => {
306306

307307
// cross folder update not supported when folder type is "Collaboration"
308308
let crossFolderErrorResp = await ExperimentCRUDUtils.importData(server, "Name\tDescription\nData1\tNotblank\n\tisBlank", dataTypeWithExpression, "MERGE", subfolder1Options, editorUserOptions);
309-
expect(crossFolderErrorResp.text.indexOf(CROSS_FOLDER_UPDATE_NOT_SUPPORTED_ERROR) > -1).toBeTruthy();
309+
expect(crossFolderErrorResp.text.indexOf(DUPLICATE_KEY_ERROR) > -1).toBeTruthy();
310310
crossFolderErrorResp = await ExperimentCRUDUtils.importData(server, "Name\tDescription\nData1\tNotblank", dataTypeWithExpression, "UPDATE", subfolder1Options, editorUserOptions);
311-
expect(crossFolderErrorResp.text.indexOf(CROSS_FOLDER_UPDATE_NOT_SUPPORTED_ERROR) > -1).toBeTruthy();
311+
expect(crossFolderErrorResp.text.indexOf(BOGUS_KEY_UPDATE_ERROR) > -1).toBeTruthy();
312312

313313
// bogus name
314314
bogusKeyProvidedError = await ExperimentCRUDUtils.importData(server, "Name\tDescription\nbogus\tisBogus", dataTypeWithExpression, "UPDATE", topFolderOptions, editorUserOptions);
@@ -433,4 +433,4 @@ describe('Duplicate IDs', () => {
433433
expect(caseInsensitive(dataResults[1], 'description')).toBe('created');
434434

435435
});
436-
});
436+
});

0 commit comments

Comments
 (0)