Skip to content
Draft
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
2 changes: 0 additions & 2 deletions api/src/org/labkey/api/action/SpringActionController.java
Original file line number Diff line number Diff line change
Expand Up @@ -1257,8 +1257,6 @@ public static void checkForMutatingSql(Supplier<String> mutatingSqlSupplier)
Class<?> actionClass = getActionForThread();
if (null == actionClass)
return;
if (actionClass.getName().contains("JunitController"))
return;

ViewContext vc = HttpView.currentContext();
boolean readonly = false;
Expand Down
18 changes: 17 additions & 1 deletion api/src/org/labkey/api/view/ViewContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,18 @@ public static StackResetter pushMockViewContext(User user, Container c, ActionUR
return new StackResetter(context, stackSize);
}

/**
* Ensures a view context is available without disturbing one that's already there: if a view is
* already on the stack, returns it wrapped in a no-op resetter; otherwise pushes a new mock context
* via {@link #pushMockViewContext}.
*/
public static StackResetter ensureViewContext(User user, Container c, ActionURL url)
{
if (HttpView.hasCurrentView())
return new StackResetter(HttpView.currentContext(), HttpView.getStackSize());
else
return pushMockViewContext(user, c, url);
}

// Needed by background threads that call entrypoints that require ViewContexts
// TODO: Well-behaved interfaces should not take ViewContexts -- clean up query, et al to remove ViewContext params
Expand All @@ -178,7 +190,11 @@ public static ViewContext getMockViewContext(User user, Container c, ActionURL u
if (null != url)
context.setBindPropertyValues(url.getPropertyValues());

HttpServletRequest request = ViewServlet.mockRequest("GET", url, user, null, null);
// Inherit current request's method, if present.
HttpServletRequest currentRequest = HttpView.currentRequest();
String mockRequestMethod = pushViewContext && currentRequest != null ? currentRequest.getMethod() : "GET";

HttpServletRequest request = ViewServlet.mockRequest(mockRequestMethod, url, user, null, null);
context.setRequest(request);

// Major hack -- QueryView needs the context pushed onto the ViewContext stack in thread local
Expand Down
41 changes: 39 additions & 2 deletions core/src/org/labkey/core/junit/JunitController.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@
import org.junit.runner.notification.Failure;
import org.labkey.api.action.ApiResponse;
import org.labkey.api.action.ApiSimpleResponse;
import org.labkey.api.action.BaseViewAction;
import org.labkey.api.action.MutatingApiAction;
import org.labkey.api.action.NavTrailAction;
import org.labkey.api.action.PermissionCheckableAction;
import org.labkey.api.action.ReadOnlyApiAction;
import org.labkey.api.action.SimpleViewAction;
Expand All @@ -41,6 +43,7 @@
import org.labkey.api.action.StatusReportingRunnable;
import org.labkey.api.action.StatusReportingRunnableAction;
import org.labkey.api.jsp.JspTest;
import org.labkey.api.security.MethodsAllowed;
import org.labkey.api.security.RequiresNoPermission;
import org.labkey.api.security.RequiresSiteAdmin;
import org.labkey.api.security.User;
Expand All @@ -56,8 +59,11 @@
import org.labkey.api.view.NotFoundException;
import org.labkey.api.view.template.PageConfig;
import org.springframework.validation.BindException;
import org.springframework.validation.Errors;
import org.springframework.web.servlet.ModelAndView;

import static org.labkey.api.util.HttpUtil.Method.POST;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
Expand Down Expand Up @@ -144,8 +150,38 @@ static public TestWhen.When getScope(Class cls)
}


/**
* Like {@link SimpleViewAction} — one code path, no form redisplay — but POST-only and not one of
* the types {@link SpringActionController#checkForMutatingSql} treats as unconditionally read-only,
* so subclasses can legitimately run mutating SQL.
*/
@MethodsAllowed(POST)
private abstract static class SimplePostViewAction<FORM> extends BaseViewAction<FORM> implements NavTrailAction
{
@Override
public final ModelAndView handleRequest() throws Exception
{
BindException errors = defaultBindParameters(getPropertyValues());
return getView((FORM) errors.getTarget(), errors);
}

@Override
protected final String getCommandClassMethodName()
{
return "getView";
}

@Override
public final void validate(Object target, Errors errors)
{
}

public abstract ModelAndView getView(FORM form, BindException errors) throws Exception;
}


@RequiresSiteAdmin
public class RunAction extends SimpleViewAction<TestForm>
public class RunAction extends SimplePostViewAction<TestForm>
{
@Override
public ModelAndView getView(TestForm form, BindException errors) throws Exception
Expand Down Expand Up @@ -220,7 +256,7 @@ public void addNavTrail(NavTree root)
private static final String RESULTS_SESSION_KEY = "JUnit_Results";

@RequiresSiteAdmin
public static class Run3Action extends SimpleViewAction<TestForm>
public static class Run3Action extends SimplePostViewAction<TestForm>
{
@Override
public ModelAndView getView(TestForm form, BindException errors) throws Exception
Expand Down Expand Up @@ -300,6 +336,7 @@ public void addNavTrail(NavTree root)


@RequiresSiteAdmin
@MethodsAllowed(POST)
public static class Run2Action extends StatusReportingRunnableAction
{
private List<Class<?>> getTestClasses(TestForm form)
Expand Down
33 changes: 23 additions & 10 deletions core/src/org/labkey/core/junit/runner.jsp
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,26 @@
<%@ page import="org.labkey.core.junit.JunitController.Run2Action" %>
<%@ page import="org.labkey.core.junit.JunitController.Run3Action" %>
<%@ page import="org.labkey.core.junit.JunitController.RunAction" %>
<%@ page import="java.util.stream.Stream" %>
<%@ page import="static org.labkey.api.util.DOM.*" %>
<%@ page import="static org.labkey.api.util.DOM.Attribute.*" %>
<%@ page import="static org.labkey.api.util.HtmlString.NBSP" %>
<%@ page extends="org.labkey.api.jsp.JspBase" %>
<%!
// Parameterized (and other Suite-based) runners nest a level of per-invocation Descriptions between the
// class and its test methods; flatten to leaves so every test method renders at the same list level.
private Stream<Description> leafDescriptions(Description desc)
{
return desc.isTest() ? Stream.of(desc) : desc.getChildren().stream().flatMap(this::leafDescriptions);
}

private Renderable renderLeaf(Description desc, ActionURL testCaseURL)
{
return LI(desc.getMethodName() != null
? simpleLink(desc.getMethodName(), testCaseURL.clone().addParameter("methodName", desc.getMethodName())).usePost()
: desc.toString());
}
%>
<%
JspView<JUnitViewBean> me = HttpView.currentView();
JUnitViewBean bean = me.getModelBean();
Expand Down Expand Up @@ -76,26 +92,26 @@

NBSP, "\u22EE", NBSP,

button("Run All").href(new ActionURL(RunAction.class, getContainer())),
button("Run All").href(new ActionURL(RunAction.class, getContainer())).usePost(),
NBSP,
button("Run BVT").href(new ActionURL(RunAction.class, getContainer()).addParameter("when", "BVT")),
button("Run BVT").href(new ActionURL(RunAction.class, getContainer()).addParameter("when", "BVT")).usePost(),
NBSP,
button("Run DRT").href(new ActionURL(RunAction.class, getContainer()).addParameter("when", "DRT")),
button("Run DRT").href(new ActionURL(RunAction.class, getContainer()).addParameter("when", "DRT")).usePost(),

NBSP, "\u22EE", NBSP,

LK.FORM(at(style, "display:inline-block;", name, "run2", action, new ActionURL(Run2Action.class, getContainer()), method, "POST"),
button("Run In Background #1 (Experimental)").submit(true)),
NBSP,
button("Run In Background #2 (Experimental)").href(new ActionURL(Run3Action.class, getContainer()))
button("Run In Background #2 (Experimental)").href(new ActionURL(Run3Action.class, getContainer())).usePost()
),
HR()).appendTo(out);

}

DIV(testCases.keySet().stream().map(module ->
DETAILS(at(open, true),
SUMMARY(A(at(href, new ActionURL(RunAction.class, getContainer()).addParameter("module", module)), module)),
SUMMARY(simpleLink(module, new ActionURL(RunAction.class, getContainer()).addParameter("module", module)).usePost()),
DIV(cl("module-details"), testCases.get(module).stream().map(clazz -> {
Runner runner = Request.aClass(clazz).getRunner();
Description desc = runner.getDescription();
Expand All @@ -105,13 +121,10 @@
return DIV(cl("labkey-indented"),
DETAILS(
SUMMARY(
A(at(href, testCaseURL.getLocalURIString()), displayName),
simpleLink(displayName, testCaseURL).usePost(),
showRunButtons ? SPAN(cl("scope-tag", JunitController.getScope(clazz).name()), JunitController.getScope(clazz).name()) : null,
(desc.testCount() > 1 ? SPAN(cl("test-count"), "(" + desc.testCount() + ")") : "")),
UL(desc.getChildren().stream().map(
child -> LI(child.getMethodName() != null
? A(at(href, testCaseURL.clone().addParameter("methodName", child.getMethodName())), child.getMethodName())
: child.toString())))
UL(leafDescriptions(desc).map(child -> renderLeaf(child, testCaseURL)))

));
}))
Expand Down
25 changes: 2 additions & 23 deletions study/src/org/labkey/study/assay/StudyPublishManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -1118,7 +1118,7 @@ public void autoLinkDerivedSamples(ExpSampleType sampleType, List<Long> keys, Co
if (sampleType != null && sampleType.getAutoLinkTargetContainer() != null)
{
// Issue 51454 : QueryView needs a view context to initialize properly. Ensure a mock view context when running in the background
try (EnsureViewContext ignore = new EnsureViewContext(container, user))
try (ViewContext.StackResetter _ = ViewContext.ensureViewContext(user, container, new ActionURL()))
{
// attempt to auto link the results
QuerySettings qs = new QuerySettings(new MutablePropertyValues(), QueryView.DATAREGIONNAME_DEFAULT);
Expand Down Expand Up @@ -1208,7 +1208,7 @@ public void autoLinkSamples(ExpSampleType sampleType, List<Map<FieldKey, Object>
if (validStudies.contains(study))
{
// Issue 49253 : QueryView needs a view context to initialize properly. Ensure a mock view context when running in the background
try (EnsureViewContext ignore = new EnsureViewContext(container, user))
try (ViewContext.StackResetter _ = ViewContext.ensureViewContext(user, container, new ActionURL()))
{
LOG.debug("Resolved target study in container {} for auto-linking with {} from container {}", targetContainerPath, sampleTypeName, containerPath);
List<Map<String, Object>> dataMaps = new ArrayList<>();
Expand Down Expand Up @@ -1751,25 +1751,4 @@ private void getViewColumns(UserSchema userSchema, QuerySettings qs, Map<FieldKe
}
}

private static class EnsureViewContext implements Closeable
{
private final boolean _hasCurrentView;
private final int _stackSize;

private EnsureViewContext(Container container, User user)
{
_hasCurrentView = HttpView.hasCurrentView();
_stackSize = HttpView.getStackSize();

if (!_hasCurrentView)
ViewContext.getMockViewContext(user, container, new ActionURL(), true);
}

@Override
public void close()
{
if (!_hasCurrentView)
HttpView.resetStackSize(_stackSize);
}
}
}