Commit f54eeba9 authored by Guillaume Cornut's avatar Guillaume Cornut Committed by Raphaël Flores
Browse files

fix: Remove unnecessary spring profile. Fix angular base href.

parent 74d039f7
......@@ -68,7 +68,7 @@ Otherwise, for the complete server (backend APIs + frontend interface), you can
./gradlew assemble && java -jar backend/build/libs/gpds.jar
```
The server should then be accessible at http://localhost:8060/gnpis/gpds
The server should then be accessible at http://localhost:8380/gnpis/gpds
## Run frontend development server
......@@ -110,18 +110,12 @@ If such a configuration is not found, it will then fallback to the local `applic
To avoid running the Spring Cloud config server every time when developing the application,
all the properties are still available in `application.yml` even if they are configured on the remote Spring Cloud server as well.
If you want to use the Spring Cloud config app locally,
see https://forgemia.inra.fr/urgi-is/data-discovery-config
The configuration is currently only read on startup,
meaning the application has to be rebooted if the configuration is changed on the Spring Cloud server.
For a dynamic reload without restarting the application,
see http://cloud.spring.io/spring-cloud-static/Finchley.SR1/single/spring-cloud.html#refresh-scope
to check what has to be changed.
In case of testing configuration from the config server, one may use a dedicated branch on `data-discovery-config` project
and append the `--spring.cloud.config.label=<branch name to test>` parameter when starting the application's executable jar.
More info on how pass a parameter to a Spring Boot app:
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config
If you want to use a Spring Cloud configuration server, please refer to
https://spring.io/guides/gs/centralized-configuration/
See the [`data-discovery-config` README.md file](https://forgemia.inra.fr/urgi-is/data-discovery-config/blob/master/README.md) for the list of application environments (server ports and context paths).
package fr.inra.urgi.gpds.filter;
import com.google.common.base.Charsets;
import com.google.common.io.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
/**
* Filter that forwards all GET requests to non-static and non-api resources to index.html. This filter is necessary
* to support deep-linking for URLs generated by the Angular router.
* Filter that intercepts all request to potential Angular routes
* (ex: /studies/ID) to send back the Angular `index.html` file with a correct
* base href set to the spring server context path.
*
* Potential angular routes are devised by process of elimination:
* - They should be GET requests
* - They should not end with common static file suffixes {@link AngularRouteFilter#STATIC_SUFFIXES}
* - They should not start with API prefixes {@link AngularRouteFilter#API_PREFIXES}
*
* <p>
* Adapted from data-discovery
*
......@@ -18,21 +31,27 @@ import java.util.Arrays;
*/
@Component
@WebFilter("/*")
public class IndexFilter implements Filter {
public class AngularRouteFilter implements Filter {
private static final String[] API_PREFIXES = {
"/brapi/v1", "/gnpis/v1", "/actuator", "/api-docs", "/v2/api-docs", "/swagger-resources"
};
private static final String[] STATIC_FILES = {
"/index.html", "/swagger-ui.html"
"/brapi/v1", "/gnpis/v1", "/actuator", "/v2/api-docs", "/swagger-resources"
};
private static final String[] STATIC_SUFFIXES = {
".js", ".css", ".ico", ".png", ".jpg", ".gif", ".eot", ".svg",
".html", ".js", ".css", ".ico", ".png", ".jpg", ".gif", ".eot", ".svg",
".woff2", ".ttf", ".woff"
};
@Value("${server.servlet.context-path}")
private String serverContextPath;
private final ResourceLoader resourceLoader;
@Autowired
public AngularRouteFilter(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
@Override
public void doFilter(
ServletRequest req,
......@@ -40,15 +59,32 @@ public class IndexFilter implements Filter {
FilterChain chain
) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
if (mustForward(request)) {
request.getRequestDispatcher("/index.html").forward(request, response);
if (isAngularRoute(request)) {
// Angular route
InputStream inputStream = resourceLoader.getResource("classpath:static/index.html").getInputStream();
ByteSource byteSource = new ByteSource() {
@Override
public InputStream openStream() {
return inputStream;
}
};
String content = byteSource.asCharSource(Charsets.UTF_8).read();
String replacedContent = content.replace(
"<base href=\"./\">",
"<base href=\"" + serverContextPath + "/\">"
);
response.getWriter().write(replacedContent);
return;
}
// Otherwise nothing to do
chain.doFilter(request, response);
}
private boolean mustForward(HttpServletRequest request) {
private boolean isAngularRoute(HttpServletRequest request) {
if (!request.getMethod().equals("GET")) {
return false;
}
......@@ -60,13 +96,11 @@ public class IndexFilter implements Filter {
return !isApiOrStaticResource(uri);
}
private boolean isApiOrStaticResource(String uri) {
private boolean isApiOrStaticResource(String relativePath) {
// Starts with API prefix
return Arrays.stream(API_PREFIXES).anyMatch(uri::startsWith)
// or is static file
|| Arrays.asList(STATIC_FILES).contains(uri)
return Arrays.stream(API_PREFIXES).anyMatch(relativePath::startsWith)
// or has static file suffix
|| Arrays.stream(STATIC_SUFFIXES).anyMatch(uri::endsWith);
|| Arrays.stream(STATIC_SUFFIXES).anyMatch(relativePath::endsWith);
}
@Override
......
......@@ -40,15 +40,6 @@ public class AuthenticationFilter implements Filter {
public void doFilter(
ServletRequest req, ServletResponse resp, FilterChain chain
) throws IOException, ServletException {
// get web login
String webUserLogin = ((HttpServletRequest) req).getRemoteUser();
logger.debug(
"\n" +
"*********************************************\n" +
" Applying user credentials for " + webUserLogin + "\n" +
"*********************************************"
);
final String authorization = ((HttpServletRequest) req).getHeader("Authorization");
if (authorization != null && authorization.startsWith("Basic")) {
......@@ -57,6 +48,8 @@ public class AuthenticationFilter implements Filter {
String authCode = new String(BaseEncoding.base64().decode(base64Credentials), Charsets.UTF_8);
final String userName = authCode.split(":", 2)[0];
logger.debug("Intercepting HTTP Authorization with user: " + userName);
AuthenticationStore.set(userName);
}
......
......@@ -12,15 +12,6 @@ management:
exposure:
include: '*'
server:
compression:
enabled: true
mime-types:
- application/json
- application/javascript
- text/html
- text/css
logging.level:
root: ERROR
org.springframework:
......@@ -28,11 +19,6 @@ logging.level:
web.client.RestTemplate: DEBUG
fr.inra: DEBUG
---
spring:
profiles: gnpis
cloud.config.name: gpds
gpds:
elasticsearch-alias-template:
gnpis_{source}_{documentType}_5432_scratchy-group{groupId}
......@@ -52,7 +38,14 @@ gpds:
security-user-group-ws-token:
server:
port: 8060
compression:
enabled: true
mime-types:
- application/json
- application/javascript
- text/html
- text/css
port: 8380
servlet:
context-path: /gnpis/gpds
context-path: /gnpis-dev/gpds
spring:
application:
name: gpds
application.name: gpds
cloud:
config:
uri: ${SPRING_CONFIG_URI:http://localhost:8888}
profiles:
active: gnpis
package fr.inra.urgi.gpds.filter;
import fr.inra.urgi.gpds.api.gnpis.v1.DataDiscoveryController;
import fr.inra.urgi.gpds.Application;
import fr.inra.urgi.gpds.config.SecurityConfig;
import fr.inra.urgi.gpds.repository.es.DataDiscoveryRepository;
import fr.inra.urgi.gpds.repository.file.DataSourceRepository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import java.io.ByteArrayInputStream;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.forwardedUrl;
/**
* Unit tests for {@link IndexFilter}
* Unit tests for {@link AngularRouteFilter}
*
* @author gcornut
*/
@ExtendWith(SpringExtension.class)
@WebMvcTest(controllers = DataDiscoveryController.class)
@Import(SecurityConfig.class)
class IndexFilterTest {
@SpringBootTest(classes = Application.class)
class AngularRouteFilterTest {
@MockBean
private DataDiscoveryRepository repository;
@Autowired
private WebApplicationContext context;
@MockBean
private DataSourceRepository dataSourceRepository;
private ResourceLoader resourceLoader;
@Autowired
private MockMvc mockMvc;
private AngularRouteFilter filter;
@BeforeEach
void setUp() {
filter = new AngularRouteFilter(resourceLoader);
mockMvc = MockMvcBuilders.webAppContextSetup(context)
.addFilter(filter, "/*")
.build();
}
@ParameterizedTest
@ValueSource(strings = {
// Static files
......@@ -64,7 +83,23 @@ class IndexFilterTest {
"/germplasm/bar",
})
void shouldForward(String url) throws Exception {
mockMvc.perform(get(url)).andExpect(forwardedUrl("/index.html"));
String indexBefore = "<html>\n" +
" <base href=\"./\">\n" +
"</html>";
String indexAfter = "<html>\n" +
" <base href=\"/gnpis-test/gpds/\">\n" +
"</html>";
ReflectionTestUtils.setField(filter, "serverContextPath", "/gnpis-test/gpds");
Resource mockResource = mock(Resource.class);
when(mockResource.getInputStream())
.thenReturn(new ByteArrayInputStream(indexBefore.getBytes()));
when(resourceLoader.getResource(anyString()))
.thenReturn(mockResource);
mockMvc.perform(get(url))
.andExpect(content().string(indexAfter));
}
}
......@@ -42,7 +42,7 @@
"node_modules/leaflet.markercluster/dist/MarkerCluster.Default.css"
],
"scripts": [],
"baseHref": "/gnpis/gpds/"
"baseHref": "./"
},
"configurations": {
"production": {
......
......@@ -4,7 +4,7 @@ const PROXY_CONFIG = [
"/gnpis/gpds/brapi",
"/gnpis/gpds/gnpis",
],
target: "http://localhost:8060",
target: "http://localhost:8380",
secure: false
}
];
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment