mirror of
https://github.com/scm-manager/scm-manager.git
synced 2026-07-06 20:08:38 +02:00
allow changing context path, by using a template for index.html
This commit is contained in:
@@ -8,20 +8,11 @@
|
||||
manifest.json provides metadata used when your web app is added to the
|
||||
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="/scm/manifest.json">
|
||||
<link rel="shortcut icon" href="/scm/favicon.ico">
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
<link rel="manifest" href="{{ contextPath }}/manifest.json">
|
||||
<link rel="shortcut icon" href="{{ contextPath }}/favicon.ico">
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<base href="/scm">
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="/scm/styles/scm.css">
|
||||
<base href="{{ contextPath }}">
|
||||
<link rel="stylesheet" type="text/css" href="{{ contextPath }}/styles/scm.css">
|
||||
|
||||
<title>SCM-Manager</title>
|
||||
</head>
|
||||
@@ -41,9 +32,9 @@
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
<script>
|
||||
window.ctxPath = "/scm";
|
||||
window.ctxPath = "{{ contextPath }}";
|
||||
</script>
|
||||
<script src="/scm/vendor.bundle.js"></script>
|
||||
<script src="/scm/scm-ui.bundle.js"></script>
|
||||
<script src="{{ contextPath }}/vendor.bundle.js"></script>
|
||||
<script src="{{ contextPath }}/scm-ui.bundle.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,27 +0,0 @@
|
||||
package sonia.scm;
|
||||
|
||||
import sonia.scm.util.HttpUtil;
|
||||
|
||||
import javax.servlet.RequestDispatcher;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* This dispatcher forwards every request to the index.html of the application.
|
||||
*
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public class ForwardingPushStateDispatcher implements PushStateDispatcher {
|
||||
@Override
|
||||
public void dispatch(HttpServletRequest request, HttpServletResponse response, String uri) throws IOException {
|
||||
String path = HttpUtil.append(request.getContextPath(), "index.html");
|
||||
RequestDispatcher dispatcher = request.getRequestDispatcher(path);
|
||||
try {
|
||||
dispatcher.forward(request, response);
|
||||
} catch (ServletException e) {
|
||||
throw new IOException("failed to forward request", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,12 +3,13 @@ package sonia.scm;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Strings;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Provider;
|
||||
|
||||
/**
|
||||
* Injection Provider for the {@link PushStateDispatcher}. The provider will return a {@link ProxyPushStateDispatcher}
|
||||
* if the system property {@code PushStateDispatcherProvider#PROPERTY_TARGET} is set to a proxy target url, otherwise
|
||||
* a {@link ForwardingPushStateDispatcher} is used.
|
||||
* a {@link TemplatingPushStateDispatcher} is used.
|
||||
*
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@@ -17,11 +18,18 @@ public class PushStateDispatcherProvider implements Provider<PushStateDispatcher
|
||||
@VisibleForTesting
|
||||
static final String PROPERTY_TARGET = "sonia.scm.ui.proxy";
|
||||
|
||||
private Provider<TemplatingPushStateDispatcher> templatingPushStateDispatcherProvider;
|
||||
|
||||
@Inject
|
||||
public PushStateDispatcherProvider(Provider<TemplatingPushStateDispatcher> templatingPushStateDispatcherProvider) {
|
||||
this.templatingPushStateDispatcherProvider = templatingPushStateDispatcherProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PushStateDispatcher get() {
|
||||
String target = System.getProperty(PROPERTY_TARGET);
|
||||
if (Strings.isNullOrEmpty(target)) {
|
||||
return new ForwardingPushStateDispatcher();
|
||||
return templatingPushStateDispatcherProvider.get();
|
||||
}
|
||||
return new ProxyPushStateDispatcher(target);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
package sonia.scm;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import sonia.scm.template.Template;
|
||||
import sonia.scm.template.TemplateEngine;
|
||||
import sonia.scm.template.TemplateEngineFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
|
||||
/**
|
||||
* This dispatcher renders the /index.mustache template, which is merged in from the scm-ui package.
|
||||
*
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public class TemplatingPushStateDispatcher implements PushStateDispatcher {
|
||||
|
||||
@VisibleForTesting
|
||||
static final String TEMPLATE = "/index.mustache";
|
||||
|
||||
private final TemplateEngine templateEngine;
|
||||
|
||||
@Inject
|
||||
public TemplatingPushStateDispatcher(TemplateEngineFactory templateEngineFactory) {
|
||||
this(templateEngineFactory.getDefaultEngine());
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
TemplatingPushStateDispatcher(TemplateEngine templateEngine) {
|
||||
this.templateEngine = templateEngine;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatch(HttpServletRequest request, HttpServletResponse response, String uri) throws IOException {
|
||||
response.setContentType("text/html");
|
||||
response.setCharacterEncoding("UTF-8");
|
||||
|
||||
Template template = templateEngine.getTemplate(TEMPLATE);
|
||||
try (Writer writer = response.getWriter()) {
|
||||
template.execute(writer, new IndexHtmlModel(request));
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static class IndexHtmlModel {
|
||||
|
||||
private final HttpServletRequest request;
|
||||
|
||||
private IndexHtmlModel(HttpServletRequest request) {
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
public String getContextPath() {
|
||||
return request.getContextPath();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
package sonia.scm;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import javax.servlet.RequestDispatcher;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class ForwardingPushStateDispatcherTest {
|
||||
|
||||
@Mock
|
||||
private HttpServletRequest request;
|
||||
|
||||
@Mock
|
||||
private RequestDispatcher requestDispatcher;
|
||||
|
||||
@Mock
|
||||
private HttpServletResponse response;
|
||||
|
||||
private ForwardingPushStateDispatcher dispatcher = new ForwardingPushStateDispatcher();
|
||||
|
||||
@Test
|
||||
public void testDispatch() throws ServletException, IOException {
|
||||
when(request.getContextPath()).thenReturn("");
|
||||
when(request.getRequestDispatcher("/index.html")).thenReturn(requestDispatcher);
|
||||
|
||||
dispatcher.dispatch(request, response, "/something");
|
||||
|
||||
verify(requestDispatcher).forward(request, response);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDispatchWithContextPath() throws ServletException, IOException {
|
||||
when(request.getContextPath()).thenReturn("/scm");
|
||||
when(request.getRequestDispatcher("/scm/index.html")).thenReturn(requestDispatcher);
|
||||
|
||||
dispatcher.dispatch(request, response, "/something");
|
||||
|
||||
verify(requestDispatcher).forward(request, response);
|
||||
}
|
||||
|
||||
@Test(expected = IOException.class)
|
||||
public void testWrapServletException() throws ServletException, IOException {
|
||||
when(request.getContextPath()).thenReturn("");
|
||||
when(request.getRequestDispatcher("/index.html")).thenReturn(requestDispatcher);
|
||||
doThrow(ServletException.class).when(requestDispatcher).forward(request, response);
|
||||
|
||||
dispatcher.dispatch(request, response, "/something");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,14 +1,23 @@
|
||||
package sonia.scm;
|
||||
|
||||
import com.google.inject.util.Providers;
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import sonia.scm.template.TemplateEngine;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class PushStateDispatcherProviderTest {
|
||||
|
||||
private PushStateDispatcherProvider provider = new PushStateDispatcherProvider();
|
||||
@Mock
|
||||
private TemplateEngine templateEngine;
|
||||
|
||||
private PushStateDispatcherProvider provider = new PushStateDispatcherProvider(
|
||||
Providers.of(new TemplatingPushStateDispatcher(templateEngine))
|
||||
);
|
||||
|
||||
@Test
|
||||
public void testGetProxyPushStateWithPropertySet() {
|
||||
@@ -20,7 +29,7 @@ public class PushStateDispatcherProviderTest {
|
||||
@Test
|
||||
public void testGetProxyPushStateWithoutProperty() {
|
||||
PushStateDispatcher dispatcher = provider.get();
|
||||
Assertions.assertThat(dispatcher).isInstanceOf(ForwardingPushStateDispatcher.class);
|
||||
Assertions.assertThat(dispatcher).isInstanceOf(TemplatingPushStateDispatcher.class);
|
||||
}
|
||||
|
||||
@After
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
package sonia.scm;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import sonia.scm.template.Template;
|
||||
import sonia.scm.template.TemplateEngine;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class TemplatingPushStateDispatcherTest {
|
||||
|
||||
@Mock
|
||||
private HttpServletRequest request;
|
||||
|
||||
@Mock
|
||||
private HttpServletResponse response;
|
||||
|
||||
@Mock
|
||||
private TemplateEngine templateEngine;
|
||||
|
||||
@Mock
|
||||
private Template template;
|
||||
|
||||
private TemplatingPushStateDispatcher dispatcher;
|
||||
|
||||
@Before
|
||||
public void setUpMocks() {
|
||||
dispatcher = new TemplatingPushStateDispatcher(templateEngine);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDispatch() throws IOException {
|
||||
when(request.getContextPath()).thenReturn("/scm");
|
||||
when(templateEngine.getTemplate(TemplatingPushStateDispatcher.TEMPLATE)).thenReturn(template);
|
||||
|
||||
when(response.getWriter()).thenReturn(new PrintWriter(new StringWriter()));
|
||||
|
||||
dispatcher.dispatch(request, response, "/someurl");
|
||||
|
||||
verify(response).setContentType("text/html");
|
||||
verify(response).setCharacterEncoding("UTF-8");
|
||||
|
||||
ArgumentCaptor<Object> captor = ArgumentCaptor.forClass(Object.class);
|
||||
|
||||
verify(template).execute(any(Writer.class), captor.capture());
|
||||
|
||||
TemplatingPushStateDispatcher.IndexHtmlModel model = (TemplatingPushStateDispatcher.IndexHtmlModel) captor.getValue();
|
||||
assertEquals("/scm", model.getContextPath());
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user