Skip to content
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
6 changes: 2 additions & 4 deletions api/src/org/labkey/api/action/BaseViewAction.java
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@
import org.springframework.web.bind.ServletRequestDataBinder;
import org.springframework.web.bind.ServletRequestParameterPropertyValues;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;

import java.beans.PropertyDescriptor;
Expand All @@ -76,7 +75,6 @@
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
Expand Down Expand Up @@ -432,13 +430,13 @@ static BindingErrorProcessor getBindingErrorProcessor(final BindingErrorProcesso
return new BindingErrorProcessor()
{
@Override
public void processMissingFieldError(String missingField, BindingResult bindingResult)
public void processMissingFieldError(@NotNull String missingField, @NotNull BindingResult bindingResult)
{
defaultBEP.processMissingFieldError(missingField, bindingResult);
}

@Override
public void processPropertyAccessException(PropertyAccessException ex, BindingResult bindingResult)
public void processPropertyAccessException(@NotNull PropertyAccessException ex, @NotNull BindingResult bindingResult)
{
Object newValue = ex.getPropertyChangeEvent().getNewValue();
if (newValue instanceof String)
Expand Down
24 changes: 20 additions & 4 deletions api/src/org/labkey/api/action/FormViewAction.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,23 @@

package org.labkey.api.action;

import org.jetbrains.annotations.NotNull;
import org.labkey.api.data.ObjectFactory;
import org.labkey.api.miniprofiler.MiniProfiler;
import org.labkey.api.miniprofiler.Timing;
import org.labkey.api.util.ExceptionUtil;
import org.labkey.api.util.URLHelper;
import org.labkey.api.view.HttpView;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.PropertyValues;
import org.springframework.validation.BindException;
import org.springframework.validation.Errors;
import org.springframework.validation.ObjectError;
import org.springframework.web.servlet.ModelAndView;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
* Is this better than BaseCommandController? Probably not, but it understands TableViewForm.
Expand Down Expand Up @@ -116,7 +121,7 @@ public ModelAndView handleRequest(FORM form, BindException errors) throws Except
if (errors != null && errors.hasErrors())
{
StringBuilder errorTextBuilder = new StringBuilder();
String newLine = System.getProperty("line.separator");
String newLine = System.lineSeparator();
List<ObjectError> errorsList = errors.getAllErrors();

for (int i = 0; i < errorsList.size(); i++)
Expand All @@ -136,7 +141,6 @@ public ModelAndView handleRequest(FORM form, BindException errors) throws Except
}
}


@Override
protected String getCommandClassMethodName()
{
Expand All @@ -145,11 +149,23 @@ protected String getCommandClassMethodName()

public BindException bindParameters(PropertyValues m) throws Exception
{
return defaultBindParameters(getCommand(), m);
Class<?> commandClass = getCommandClass();
return commandClass.isRecord() ? defaultBindParametersToRecord(commandClass, m) : defaultBindParameters(getCommand(), m);
}

// Very simple binding for Java records: no support for binding errors, arrays, lists, disallowed fields, etc.
private <R> BindException defaultBindParametersToRecord(Class<R> recordClass, PropertyValues m)
{
ObjectFactory<R> factory = ObjectFactory.Registry.getFactory(recordClass);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Standard form binding goes through getPropertyValuesForFormBinding(). Seems wise to do that here too, even if records are less likely to have problems

Map<String, Object> map = m.stream()
.filter(pv -> pv.getValue() != null)
.collect(Collectors.toMap(PropertyValue::getName, PropertyValue::getValue));
R record = factory.fromMap(map);
return new NullSafeBindException(record, "Form");
}

@Override
public void validate(Object target, Errors errors)
public void validate(@NotNull Object target, @NotNull Errors errors)
{
if (target instanceof HasValidator)
{
Expand Down
6 changes: 6 additions & 0 deletions api/src/org/labkey/api/data/TableInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,12 @@ void addColumn(ColumnInfo column)
{
columns.add(column);
}

public String display()
{
String display = name + " " + columns.stream().map(ColumnInfo::getName).toList();
return filterCondition == null ? display : display + " + " + filterCondition;
}
}

/** Get a list of columns that specifies a unique key, may return the same result as getPKColumns()
Expand Down
52 changes: 26 additions & 26 deletions core/src/org/labkey/core/junit/JunitController.java
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,10 @@ public JunitController()

public static class JUnitViewBean
{
public final Map<String, List<Class>> testCases;
public final Map<String, List<Class<?>>> testCases;
public final boolean showRunButtons;

JUnitViewBean(Map<String, List<Class>> tests, boolean buttons)
JUnitViewBean(Map<String, List<Class<?>>> tests, boolean buttons)
{
this.testCases = tests;
this.showRunButtons = buttons;
Expand Down Expand Up @@ -186,7 +186,7 @@ else if (!StringUtils.isEmpty(form.getTestCase()))
// To allow performance tests to be selected change the scope to PERFORMANCE.
form._scope = TestWhen.When.PERFORMANCE;

for (List<Class> list : JunitManager.getTestCases().values())
for (List<Class<?>> list : JunitManager.getTestCases().values())
{
list.stream()
.filter((test) -> test.getName().equals(form.getTestCase()))
Expand Down Expand Up @@ -237,14 +237,14 @@ public ModelAndView getView(TestForm form, BindException errors) throws Exceptio
}
else
{
List<Class> testClasses = getTestClasses(form);
List<Class<?>> testClasses = getTestClasses(form);
TestContext.setTestContext(getViewContext().getRequest(), getUser());
getPageConfig().setTemplate(PageConfig.Template.Dialog);
results = new LinkedList<>();
HttpServletResponse response = getViewContext().getResponse();
response.setContentType("text/plain");

for (Class testClass : testClasses)
for (Class<?> testClass : testClasses)
{
// show status. this also stops the tests if the client goes away.
response.getWriter().println(testClass.getName());
Expand All @@ -260,32 +260,32 @@ public ModelAndView getView(TestForm form, BindException errors) throws Exceptio
return view;
}

private List<Class> getTestClasses(TestForm form)
private List<Class<?>> getTestClasses(TestForm form)
{
String module = form.getModule();

if (null != module)
{
List<Class> moduleTests = JunitManager.getTestCases().get(module);
List<Class<?>> moduleTests = JunitManager.getTestCases().get(module);
if (moduleTests == null || moduleTests.isEmpty())
{
throw new NotFoundException("No tests for module: " + module);
}
return moduleTests;
}

Set<Class> allTestClasses = new LinkedHashSet<>();
Set<Class<?>> allTestClasses = new LinkedHashSet<>();
JunitManager.getTestCases()
.values()
.forEach(moduleTests -> allTestClasses.addAll(moduleTests));
.values()
.forEach(allTestClasses::addAll);

final String testCase = form.getTestCase();
if (!StringUtils.isBlank(testCase))
{
Class specifiedTest = allTestClasses.parallelStream()
.filter(clazz -> testCase.equals(clazz.getName()))
.findAny()
.orElseThrow(() -> new NotFoundException("No such test: " + testCase));
Class<?> specifiedTest = allTestClasses.parallelStream()
.filter(clazz -> testCase.equals(clazz.getName()))
.findAny()
.orElseThrow(() -> new NotFoundException("No such test: " + testCase));
return Collections.singletonList(specifiedTest);
}

Expand All @@ -302,23 +302,23 @@ public void addNavTrail(NavTree root)
@RequiresSiteAdmin
public static class Run2Action extends StatusReportingRunnableAction
{
private List<Class> getTestClasses(TestForm form)
private List<Class<?>> getTestClasses(TestForm form)
{
Map<String, List<Class>> allTestClasses = JunitManager.getTestCases();
Map<String, List<Class<?>>> allTestClasses = JunitManager.getTestCases();

String module = form.getModule();

if (null != module)
return JunitManager.getTestCases().get(module);

List<Class> testClasses = new LinkedList<>();
List<Class<?>> testClasses = new LinkedList<>();
String testCase = form.getTestCase();

if (null == testCase || !testCase.isEmpty())
{
for (String m : allTestClasses.keySet())
{
for (Class clazz : allTestClasses.get(m))
for (Class<?> clazz : allTestClasses.get(m))
{
// include test
if (null == testCase || testCase.equals(clazz.getName()))
Expand All @@ -333,7 +333,7 @@ private List<Class> getTestClasses(TestForm form)
@Override
protected StatusReportingRunnable newStatusReportingRunnable()
{
List<Class> testClasses = getTestClasses(new TestForm());
List<Class<?>> testClasses = getTestClasses(new TestForm());
List<JunitRunner.RunnerResult> results = new LinkedList<>();
return new JunitRunnable(testClasses, results, getViewContext().getRequest(), getUser());
}
Expand All @@ -344,11 +344,11 @@ private static class JunitRunnable implements StatusReportingRunnable
{
private final StatusAppender _appender;
private final Logger _log;
private final List<Class> _testClasses;
private final List<Class<?>> _testClasses;
private final List<JunitRunner.RunnerResult> _results;
private volatile boolean _running = true;

private JunitRunnable(List<Class> testClasses, List<JunitRunner.RunnerResult> results, HttpServletRequest request, User user) // TODO: Make this a Callable instead?
private JunitRunnable(List<Class<?>> testClasses, List<JunitRunner.RunnerResult> results, HttpServletRequest request, User user) // TODO: Make this a Callable instead?
{
_testClasses = testClasses;
_results = results;
Expand Down Expand Up @@ -395,14 +395,14 @@ public static class Testlist extends ReadOnlyApiAction
@Override
public ApiResponse execute(Object o, BindException errors)
{
Map<String, List<Class>> testCases = JunitManager.getTestCases();
Map<String, List<Class<?>>> testCases = JunitManager.getTestCases();

Map<String, List<Map<String, Object>>> values = new HashMap<>();
for (String module : testCases.keySet())
{
List<Map<String, Object>> tests = new ArrayList<>();
values.put("Remote " + module, tests);
for (Class<Object> clazz : testCases.get(module))
for (Class<?> clazz : testCases.get(module))
{
int timeout = TestTimeout.DEFAULT;
// Check if the test has requested a non-standard timeout
Expand Down Expand Up @@ -545,13 +545,13 @@ public void setWhen(String when)
}


private static Class findTestClass(String testCase)
private static Class<?> findTestClass(String testCase)
{
Map<String, List<Class>> testCases = JunitManager.getTestCases();
Map<String, List<Class<?>>> testCases = JunitManager.getTestCases();

for (String module : testCases.keySet())
{
for (Class clazz : testCases.get(module))
for (Class<?> clazz : testCases.get(module))
{
if (null == testCase || testCase.equals(clazz.getName()))
return clazz;
Expand Down
8 changes: 4 additions & 4 deletions core/src/org/labkey/core/junit/JunitManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,20 +35,20 @@
*/
public class JunitManager
{
public static synchronized Map<String, List<Class>> getTestCases()
public static synchronized Map<String, List<Class<?>>> getTestCases()
{
Map<String, List<Class>> testCases = new TreeMap<>();
Map<String, List<Class<?>>> testCases = new TreeMap<>();

for (Module module : ModuleLoader.getInstance().getModules())
{
Set<Class> moduleClazzes = new HashSet<>();
Set<Class<?>> moduleClazzes = new HashSet<>();

module.getIntegrationTestFactories().forEach(f -> moduleClazzes.add(f.get()));
moduleClazzes.addAll(module.getUnitTests());

if (!moduleClazzes.isEmpty())
{
List<Class> moduleClazzList = new ArrayList<>(moduleClazzes);
List<Class<?>> moduleClazzList = new ArrayList<>(moduleClazzes);
moduleClazzList.sort(Comparator.comparing(Class::getName));

testCases.put(module.getName(), Collections.unmodifiableList(moduleClazzList));
Expand Down
10 changes: 7 additions & 3 deletions devtools/src/org/labkey/devtools/DevtoolsModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,13 @@ protected void init()
addController("testsso", TestSsoController.class);
AuthenticationManager.registerProvider(new TestSsoProvider());

OptionalFeatureService.get().addExperimentalFeatureFlag(Domain.EXPERIMENTAL_FUZZ_STORAGE_NAME,
OptionalFeatureService.get().addExperimentalFeatureFlag(
Domain.EXPERIMENTAL_FUZZ_STORAGE_NAME,
"'fuzz' name of database columns used to back domain properties",
"This is dev/test feature and not intended for any production usage.",
false, true);
false,
true
);
}

@Override
Expand All @@ -87,7 +90,8 @@ public void doStartup(ModuleContext moduleContext)
public @NotNull Set<Class<?>> getIntegrationTests()
{
return Set.of(
TestController.JsonInputLimitTest.class
TestController.JsonInputLimitTest.class,
ToolsController.TestCase.class
);
}
}
Loading
Loading