Commit b52fc744 authored by Sonia Zorba's avatar Sonia Zorba
Browse files

Implemented list of files upload

parent a12a8a15
Loading
Loading
Loading
Loading
Loading
+27 −0
Original line number Diff line number Diff line
@@ -5,11 +5,14 @@ import java.util.List;
public class FileInfo {

    private String osRelPath;
    private String virtualPath;
    private boolean isPublic;
    private List<String> groupRead;
    private List<String> groupWrite;
    private String ownerId;
    private boolean asyncTrans;
    private List<String> acceptViews;
    private List<String> provideViews;

    public String getOsRelPath() {
        return osRelPath;
@@ -19,6 +22,14 @@ public class FileInfo {
        this.osRelPath = osRelPath;
    }

    public String getVirtualPath() {
        return virtualPath;
    }

    public void setVirtualPath(String virtualPath) {
        this.virtualPath = virtualPath;
    }

    public boolean isIsPublic() {
        return isPublic;
    }
@@ -58,4 +69,20 @@ public class FileInfo {
    public void setAsyncTrans(boolean asyncTrans) {
        this.asyncTrans = asyncTrans;
    }

    public List<String> getAcceptViews() {
        return acceptViews;
    }

    public void setAcceptViews(List<String> acceptViews) {
        this.acceptViews = acceptViews;
    }

    public List<String> getProvideViews() {
        return provideViews;
    }

    public void setProvideViews(List<String> provideViews) {
        this.provideViews = provideViews;
    }
}
+33 −1
Original line number Diff line number Diff line
package it.inaf.ia2.transfer.controller;

import it.inaf.ia2.transfer.persistence.FileDAO;
import it.inaf.ia2.transfer.persistence.ListOfFilesDAO;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
@@ -26,6 +31,9 @@ public class PutFileController {
    @Autowired
    private FileDAO fileDAO;

    @Autowired
    private ListOfFilesDAO listOfFilesDAO;

    @Autowired
    private HttpServletRequest request;

@@ -47,6 +55,30 @@ public class PutFileController {

    private void storeFile(FileInfo fileInfo, InputStream is) throws IOException {

        if (fileInfo.getAcceptViews() != null && fileInfo.getAcceptViews().contains("urn:list-of-files")) {
            storeListOfFiles(fileInfo, is);
        } else {
            storeGenericFile(fileInfo, is);
        }
    }

    private void storeListOfFiles(FileInfo fileInfo, InputStream is) throws IOException {
        List<String> filePaths = parseListOfFiles(is);
        listOfFilesDAO.createList(fileInfo.getVirtualPath(), filePaths);
    }

    private List<String> parseListOfFiles(InputStream is) throws IOException {
        List<String> filePaths = new ArrayList<>();
        try (BufferedReader br = new BufferedReader(new InputStreamReader(is))) {
            String line;
            while ((line = br.readLine()) != null && !line.isBlank()) {
                filePaths.add(line.trim());
            }
        }
        return filePaths;
    }

    private void storeGenericFile(FileInfo fileInfo, InputStream is) throws IOException {
        Path path = Path.of(pathPrefix, fileInfo.getOsRelPath());

        File file = path.toFile();
+6 −2
Original line number Diff line number Diff line
@@ -25,8 +25,9 @@ public class FileDAO {

    public Optional<FileInfo> getFileInfo(String virtualPath) {

        String sql = "select os_path, is_public, group_read, group_write, owner_id, async_trans from\n"
                + "node n join node_path p on n.node_id = p.node_id\n"
        String sql = "select os_path, is_public, group_read, group_write, owner_id, async_trans,\n"
                + "accept_views, provide_views\n"
                + "from node n join node_path p on n.node_id = p.node_id\n"
                + "and vos_path = ?";

        FileInfo fileInfo = jdbcTemplate.query(conn -> {
@@ -42,6 +43,9 @@ public class FileDAO {
                fi.setGroupWrite(toList(rs.getArray("group_write")));
                fi.setOwnerId(rs.getString("owner_id"));
                fi.setAsyncTrans(rs.getBoolean("async_trans"));
                fi.setAcceptViews(toList(rs.getArray("accept_views")));
                fi.setProvideViews(toList(rs.getArray("provide_views")));
                fi.setVirtualPath(virtualPath);
                return fi;
            }
            return null;
+91 −0
Original line number Diff line number Diff line
package it.inaf.ia2.transfer.persistence;

import java.sql.PreparedStatement;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository
public class ListOfFilesDAO {

    private final JdbcTemplate jdbcTemplate;

    @Autowired
    public ListOfFilesDAO(DataSource fileCatalogDatasource) {
        this.jdbcTemplate = new JdbcTemplate(fileCatalogDatasource);
    }

    public void createList(String vosListNodePath, List<String> vosPaths) {

        List<NodeIdPath> idPaths = getAllNodeIdPaths(vosListNodePath, vosPaths);

        if (idPaths.size() != (vosPaths.size() + 1)) {
            throw new IllegalStateException("Unable to retrieve some file identifiers from paths");
        }

        Integer listNodeId = null;

        List<Integer> nodeIds = new ArrayList<>();
        for (NodeIdPath idPath : idPaths) {
            if (vosListNodePath.equals(idPath.nodePath)) {
                listNodeId = idPath.nodeId;
            } else {
                nodeIds.add(idPath.nodeId);
            }
        }

        if (listNodeId == null) {
            throw new IllegalStateException("List node id not found for path " + vosListNodePath);
        }

        createList(listNodeId, nodeIds);
    }

    private List<NodeIdPath> getAllNodeIdPaths(String vosListNodePath, List<String> vosPaths) {
        List<String> allPaths = new ArrayList<>(vosPaths);
        allPaths.add(vosListNodePath);

        String sql = "SELECT node_id, vos_path FROM node_vos_path WHERE vos_path IN ("
                + String.join(",", Collections.nCopies(allPaths.size(), "?")) + ")";

        return jdbcTemplate.query(conn -> {
            PreparedStatement ps = conn.prepareStatement(sql);
            int i = 0;
            for (String path : allPaths) {
                ps.setString(++i, path);
            }
            return ps;
        }, (row, index) -> {
            NodeIdPath nodeIdPath = new NodeIdPath();
            nodeIdPath.nodeId = row.getInt("node_id");
            nodeIdPath.nodePath = row.getString("vos_path");
            return nodeIdPath;
        });
    }

    private void createList(int listNodeId, List<Integer> nodes) {

        String sql = "INSERT INTO list_of_files (list_node_id, node_id) VALUES "
                + String.join(",", Collections.nCopies(nodes.size(), "(?, ?)"));

        jdbcTemplate.update(conn -> {
            PreparedStatement ps = conn.prepareStatement(sql);
            int i = 0;
            for (int nodeId : nodes) {
                ps.setInt(++i, listNodeId);
                ps.setInt(++i, nodeId);
            }
            return ps;
        });
    }

    static class NodeIdPath {

        int nodeId;
        String nodePath;
    }
}
+108 −0
Original line number Diff line number Diff line
package it.inaf.ia2.transfer.controller;

import it.inaf.ia2.transfer.persistence.FileDAO;
import it.inaf.ia2.transfer.persistence.ListOfFilesDAO;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.Collections;
import java.util.Optional;
import java.util.UUID;
import org.assertj.core.util.Files;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.request.RequestPostProcessor;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@SpringBootTest
@AutoConfigureMockMvc
@TestPropertySource(properties = {"path_prefix=/tmp"})
public class PutFileControllerTest {

    @MockBean
    private FileDAO fileDao;

    @MockBean
    private ListOfFilesDAO listOfFilesDAO;

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void putGenericFile() throws Exception {

        String randomFileName = UUID.randomUUID().toString();
        createBaseFileInfo(randomFileName);

        MockMultipartFile fakeFile = new MockMultipartFile("file", "test.txt", "text/plain", "content".getBytes());

        mockMvc.perform(putMultipart("/path/to/test.txt")
                .file(fakeFile))
                .andDo(print())
                .andExpect(status().isOk());

        File file = Path.of("/tmp", randomFileName).toFile();

        assertTrue(file.exists());
        assertEquals("content", Files.contentOf(file, StandardCharsets.UTF_8));
        assertTrue(file.delete());
    }

    @Test
    public void putListOfFiles() throws Exception {

        String randomFileName = UUID.randomUUID().toString();
        FileInfo fileInfo = createBaseFileInfo(randomFileName);
        fileInfo.setAcceptViews(Collections.singletonList("urn:list-of-files"));

        String content = "/path/to/file1\n/path/to/file2";

        MockMultipartFile fakeFile = new MockMultipartFile("file", "test.txt", "text/plain", content.getBytes());

        mockMvc.perform(putMultipart("/path/to/test.txt")
                .file(fakeFile))
                .andDo(print())
                .andExpect(status().isOk());

        verify(listOfFilesDAO, times(1)).createList(any(), argThat(list -> list.size() == 2));
    }

    private FileInfo createBaseFileInfo(String fileName) {
        FileInfo fileInfo = new FileInfo();
        fileInfo.setOsRelPath(fileName);
        fileInfo.setIsPublic(false);

        when(fileDao.getFileInfo(any())).thenReturn(Optional.of(fileInfo));

        return fileInfo;
    }

    private MockMultipartHttpServletRequestBuilder putMultipart(String uri) {
        MockMultipartHttpServletRequestBuilder builder = MockMvcRequestBuilders.multipart(uri);
        builder.with(new RequestPostProcessor() {
            @Override
            public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {
                request.setMethod("PUT");
                return request;
            }
        });
        return builder;
    }
}
Loading