Commit 58f6570b authored by Sonia Zorba's avatar Sonia Zorba
Browse files

Started node listing implementation

parent 8377a853
Loading
Loading
Loading
Loading
+41 −0
Original line number Diff line number Diff line
package it.inaf.ia2.vospace.ui;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class SecurityConfig {

    private static final Logger LOG = LoggerFactory.getLogger(SecurityConfig.class);

    @Value("${cors.allowed.origin}")
    private String corsAllowedOrigin;

    /**
     * CORS are necessary only for development (API access from npm server).
     */
    @Bean
    @Profile("dev")
    public WebMvcConfigurer corsConfigurer() {

        return new WebMvcConfigurer() {

            @Override
            public void addCorsMappings(CorsRegistry registry) {

                LOG.warn("Development profile active: CORS filter enabled");

                registry.addMapping("/**")
                        .allowedOrigins(corsAllowedOrigin)
                        .allowedMethods("*")
                        .allowCredentials(true);
            }
        };
    }
}
+12 −0
Original line number Diff line number Diff line
package it.inaf.ia2.vospace.ui;

public class VOSpaceException extends RuntimeException {

    public VOSpaceException(String message) {
        super(message);
    }

    public VOSpaceException(String message, Throwable cause) {
        super(message, cause);
    }
}
+2 −2
Original line number Diff line number Diff line
@@ -4,9 +4,9 @@ import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class VospaceUiApplication {
public class VOSpaceUiApplication {

    public static void main(String[] args) {
        SpringApplication.run(VospaceUiApplication.class, args);
        SpringApplication.run(VOSpaceUiApplication.class, args);
    }
}
+121 −0
Original line number Diff line number Diff line
package it.inaf.ia2.vospace.ui.client;

import com.fasterxml.jackson.databind.ObjectMapper;
import it.inaf.ia2.vospace.ui.VOSpaceException;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.net.ConnectException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;
import java.util.Scanner;
import java.util.concurrent.CompletionException;
import java.util.function.Function;
import net.ivoa.xml.vospace.v2.Node;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class VOSpaceClient {

    private static final Logger LOG = LoggerFactory.getLogger(VOSpaceClient.class);

    private static final ObjectMapper MAPPER = new ObjectMapper();

    private final HttpClient httpClient;
    private final String baseUrl;

    public VOSpaceClient(@Value("${vospace-backend-url}") String backendUrl) {
        if (backendUrl.endsWith("/")) {
            // Remove final slash from configured URL
            backendUrl = backendUrl.substring(0, backendUrl.length() - 1);
        }
        baseUrl = backendUrl;

        httpClient = HttpClient.newBuilder()
                .followRedirects(HttpClient.Redirect.ALWAYS)
                .version(HttpClient.Version.HTTP_1_1)
                .build();
    }

    public Node getNode(String path) {

        HttpRequest request = getRequest("/nodes/" + path)
                .header("Accept", "application/json")
                .build();

        return call(request, BodyHandlers.ofInputStream(), 200, res -> parseJson(res, Node.class));
    }

    private <T, U> U call(HttpRequest request, HttpResponse.BodyHandler<T> responseBodyHandler, int expectedStatusCode, Function<T, U> responseHandler) {
        try {
            return httpClient.sendAsync(request, responseBodyHandler)
                    .thenApply(response -> {
                        if (response.statusCode() == expectedStatusCode) {
                            return response.body();
                        }
                        logServerError(request, response);
                        throw new VOSpaceException("Error calling " + request.uri().toString() + ". Server response code is " + response.statusCode());
                    })
                    .thenApply(response -> responseHandler.apply(response))
                    .join();
        } catch (CompletionException ex) {
            if (ex.getCause() != null) {
                if (ex.getCause() instanceof ConnectException) {
                    throw new VOSpaceException("Cannot connect to " + request.uri().getHost() + " on port " + request.uri().getPort());
                }
                if (ex.getCause() instanceof VOSpaceException) {
                    throw (VOSpaceException) ex.getCause();
                }
                LOG.error("Error calling " + request.uri().toString(), ex.getCause());
                throw new VOSpaceException("Error calling " + request.uri().toString(), ex.getCause());
            }
            LOG.error("Error calling " + request.uri().toString(), ex);
            throw new VOSpaceException("Error calling " + request.uri().toString(), ex);
        }
    }

    private HttpRequest.Builder getRequest(String path) {
        return HttpRequest.newBuilder(URI.create(baseUrl + path));
    }

    private static <T> T parseJson(InputStream in, Class<T> type) {
        try {
            return MAPPER.readValue(in, type);
        } catch (IOException ex) {
            LOG.error("Invalid JSON for class {}", type.getCanonicalName());
            throw new UncheckedIOException(ex);
        }
    }

    private static <T> void logServerError(HttpRequest request, HttpResponse<T> response) {
        if (response.body() instanceof String) {
            logServerErrorString(request, (HttpResponse<String>) response);
        } else if (response.body() instanceof InputStream) {
            logServerErrorInputStream(request, (HttpResponse<InputStream>) response);
        } else {
            throw new UnsupportedOperationException("Unable to log error for response body type " + response.body().getClass().getCanonicalName());
        }
    }

    private static void logServerErrorString(HttpRequest request, HttpResponse<String> response) {

        LOG.error("Error while reading " + request.uri()
                + "\nServer response status code is " + response.statusCode()
                + "\nServer response text is " + response.body());
    }

    private static void logServerErrorInputStream(HttpRequest request, HttpResponse<InputStream> response) {
        Scanner s = new Scanner(response.body()).useDelimiter("\\A");
        String responseBody = s.hasNext() ? s.next() : "";
        String error = "Error while reading " + request.uri()
                + "\nServer response status code is " + response.statusCode()
                + "\nServer response text is " + responseBody;
        LOG.error(error);
    }
}
+16 −0
Original line number Diff line number Diff line
package it.inaf.ia2.vospace.ui.controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class IndexController {

    @GetMapping(value = "/", produces = MediaType.TEXT_HTML_VALUE)
    public String index(HttpServletRequest request, HttpServletResponse response) {
        return "index.html";
    }
}
Loading