Skip to content
Snippets Groups Projects
Commit 49c676ac authored by Martin Weise's avatar Martin Weise
Browse files

added csv import service and updated the frontend accordingly

Former-commit-id: 9837c3ad
parent f3f3767b
No related branches found
No related tags found
1 merge request!42Fixed the query service tests
package at.tuwien.endpoints;
import at.tuwien.api.database.table.TableInsertDto;
import at.tuwien.exception.*;
import at.tuwien.service.ImportService;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
@Log4j2
@CrossOrigin(origins = "*")
@RestController
@RequestMapping("/api/database/{id}")
public class ImportEndpoint {
private final ImportService importService;
@Autowired
public ImportEndpoint(ImportService importService) {
this.importService = importService;
}
@Transactional
@PostMapping("/table/{tableId}/import")
@ApiOperation(value = "Insert values", notes = "Insert Data into a Table in the database.")
@ApiResponses({
@ApiResponse(code = 201, message = "Updated the table."),
@ApiResponse(code = 400, message = "The form contains invalid data."),
@ApiResponse(code = 401, message = "Not authorized to update tables."),
@ApiResponse(code = 404, message = "The table is not found in database."),
@ApiResponse(code = 415, message = "The file provided is not in csv format"),
@ApiResponse(code = 422, message = "The csv was not processible."),
})
public ResponseEntity<?> insert(@PathVariable("id") Long databaseId,
@PathVariable("tableId") Long tableId,
@Valid @ModelAttribute TableInsertDto data) throws TableNotFoundException,
TableMalformedException, DatabaseNotFoundException, ImageNotSupportedException, FileStorageException {
importService.insertFromFile(databaseId, tableId, data);
return ResponseEntity.accepted()
.build();
}
}
...@@ -31,16 +31,11 @@ public class TableEndpoint { ...@@ -31,16 +31,11 @@ public class TableEndpoint {
private final TableService tableService; private final TableService tableService;
private final TableMapper tableMapper; private final TableMapper tableMapper;
private final QueryMapper queryResultMapper;
private final ApplicationContext applicationContext;
@Autowired @Autowired
public TableEndpoint(TableService tableService, TableMapper tableMapper, QueryMapper queryResultMapper, public TableEndpoint(TableService tableService, TableMapper tableMapper) {
ApplicationContext applicationContext) {
this.tableService = tableService; this.tableService = tableService;
this.tableMapper = tableMapper; this.tableMapper = tableMapper;
this.queryResultMapper = queryResultMapper;
this.applicationContext = applicationContext;
} }
@Transactional @Transactional
...@@ -80,40 +75,6 @@ public class TableEndpoint { ...@@ -80,40 +75,6 @@ public class TableEndpoint {
.body(tableMapper.tableToTableBriefDto(table)); .body(tableMapper.tableToTableBriefDto(table));
} }
// @Transactional
// @PostMapping("/table/csv")
// @ApiOperation(value = "Create a table", notes = "Creates a file, which is given as a multipart file.")
// @ApiResponses({
// @ApiResponse(code = 201, message = "The table was created."),
// @ApiResponse(code = 400, message = "The creation form contains invalid data."),
// @ApiResponse(code = 401, message = "Not authorized to create a tables."),
// @ApiResponse(code = 404, message = "The database does not exist."),
// @ApiResponse(code = 405, message = "The container is not running."),
// @ApiResponse(code = 409, message = "The container image is not supported."),
// })
// public ResponseEntity<TableDto> createViaCsv(@PathVariable("id") Long databaseId, @RequestPart("file") MultipartFile file, @RequestPart TableCsvInformationDto headers) {
// final Table table = tableService.create(databaseId, file, headers);
// return ResponseEntity.status(HttpStatus.CREATED)
// .body(tableMapper.tableToTableDto(table));
// }
// @Transactional
// @PostMapping("/table/csv/local")
// @ApiOperation(value = "Create a table", notes = "This is done by saving a file on the shared docker filesystem and then sending the link to the file.")
// @ApiResponses({
// @ApiResponse(code = 201, message = "The table was created."),
// @ApiResponse(code = 400, message = "The creation form contains invalid data."),
// @ApiResponse(code = 401, message = "Not authorized to create a tables."),
// @ApiResponse(code = 404, message = "The database does not exist."),
// @ApiResponse(code = 405, message = "The container is not running."),
// @ApiResponse(code = 409, message = "The container image is not supported."),
// })
// public ResponseEntity<TableDto> createViaCsv(@PathVariable("id") Long databaseId, @Valid @RequestBody TableCsvInformationDto tableCSVInformation) throws IOException {
// final Table table = tableService.create(databaseId, tableCSVInformation);
// return ResponseEntity.status(HttpStatus.CREATED)
// .body(tableMapper.tableToTableDto(table));
// }
@Transactional @Transactional
@GetMapping("/table/{tableId}") @GetMapping("/table/{tableId}")
...@@ -159,40 +120,4 @@ public class TableEndpoint { ...@@ -159,40 +120,4 @@ public class TableEndpoint {
tableService.deleteTable(databaseId, tableId); tableService.deleteTable(databaseId, tableId);
} }
@Transactional
@PostMapping("/table/{tableId}")
@ApiOperation(value = "Insert values", notes = "Insert Data into a Table in the database.")
@ApiResponses({
@ApiResponse(code = 201, message = "Updated the table."),
@ApiResponse(code = 400, message = "The form contains invalid data."),
@ApiResponse(code = 401, message = "Not authorized to update tables."),
@ApiResponse(code = 404, message = "The table is not found in database."),
@ApiResponse(code = 415, message = "The file provided is not in csv format"),
@ApiResponse(code = 422, message = "The csv was not processible."),
})
public ResponseEntity<?> insert(@PathVariable("id") Long databaseId,
@PathVariable("tableId") Long tableId,
@Valid @ModelAttribute TableInsertDto data) throws TableNotFoundException,
TableMalformedException, DatabaseNotFoundException, ImageNotSupportedException, FileStorageException {
tableService.insertFromFile(databaseId, tableId, data);
return ResponseEntity.accepted()
.build();
}
// @Transactional
// @GetMapping("/table/{tableId}/data")
// @ApiOperation(value = "show data", notes = "Show all the data for a table")
// @ApiResponses({
// @ApiResponse(code = 200, message = "All tables are listed."),
// @ApiResponse(code = 401, message = "Not authorized to list all tables."),
// })
// /* FIXME: this should be a different endpoint */
// public ResponseEntity<QueryResultDto> showData(@PathVariable("id") Long databaseId,
// @PathVariable("tableId") Long tableId)
// throws DatabaseNotFoundException, ImageNotSupportedException, TableNotFoundException,
// DatabaseConnectionException, DataProcessingException {
// final QueryResultDto queryResult = tableService.showData(databaseId, tableId);
// return ResponseEntity.ok(queryResultMapper.queryResultToQueryResultDto(queryResult));
// }
} }
package at.tuwien.service;
import at.tuwien.BaseUnitTest;
import at.tuwien.api.database.table.TableCreateDto;
import at.tuwien.api.database.table.TableInsertDto;
import at.tuwien.entities.database.table.Table;
import at.tuwien.exception.*;
import at.tuwien.repository.ContainerRepository;
import at.tuwien.repository.DatabaseRepository;
import at.tuwien.repository.ImageRepository;
import at.tuwien.repository.TableRepository;
import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.command.CreateContainerResponse;
import com.github.dockerjava.api.exception.NotModifiedException;
import com.github.dockerjava.api.model.HostConfig;
import com.github.dockerjava.api.model.Network;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ResourceUtils;
import java.io.IOException;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.*;
@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)
@ExtendWith(SpringExtension.class)
@SpringBootTest
public class ImportServiceIntegrationTest extends BaseUnitTest {
@Autowired
private HostConfig hostConfig;
@Autowired
private DockerClient dockerClient;
@Autowired
private ImageRepository imageRepository;
@Autowired
private DatabaseRepository databaseRepository;
@Autowired
private ContainerRepository containerRepository;
@Autowired
private TableRepository tableRepository;
@Autowired
private ImportService dataService;
private CreateContainerResponse request;
@Transactional
@BeforeEach
public void beforeEach() throws InterruptedException {
afterEach();
/* create network */
dockerClient.createNetworkCmd()
.withName("fda-userdb")
.withIpam(new Network.Ipam()
.withConfig(new Network.Ipam.Config()
.withSubnet("172.28.0.0/16")))
.withEnableIpv6(false)
.exec();
imageRepository.save(IMAGE_1);
/* create container */
request = dockerClient.createContainerCmd(IMAGE_1_REPOSITORY + ":" + IMAGE_1_TAG)
.withEnv(IMAGE_1_ENVIRONMENT)
.withHostConfig(hostConfig.withNetworkMode("fda-userdb"))
.withName(CONTAINER_1_INTERNALNAME)
.withIpv4Address(CONTAINER_1_IP)
.withHostName(CONTAINER_1_INTERNALNAME)
.exec();
/* start container */
dockerClient.startContainerCmd(request.getId()).exec();
Thread.sleep(3000);
CONTAINER_1_HASH = request.getId();
databaseRepository.save(DATABASE_1);
databaseRepository.save(DATABASE_2);
}
@Transactional
@AfterEach
public void afterEach() {
/* stop containers and remove them */
dockerClient.listContainersCmd()
.withShowAll(true)
.exec()
.forEach(container -> {
System.out.println("DELETE CONTAINER " + Arrays.toString(container.getNames()));
try {
dockerClient.stopContainerCmd(container.getId()).exec();
} catch (NotModifiedException e) {
// ignore
}
dockerClient.removeContainerCmd(container.getId()).exec();
});
/* remove networks */
dockerClient.listNetworksCmd()
.exec()
.stream()
.filter(n -> n.getName().startsWith("fda"))
.forEach(network -> {
System.out.println("DELETE NETWORK " + network.getName());
dockerClient.removeNetworkCmd(network.getId()).exec();
});
}
private void create_table() throws ArbitraryPrimaryKeysException, DatabaseNotFoundException, ImageNotSupportedException, DataProcessingException {
final Table response = dataService.createTable(DATABASE_1_ID, TableCreateDto.builder()
.name(TABLE_2_NAME)
.description(TABLE_2_DESCRIPTION)
.columns(COLUMNS5)
.build());
}
@Test
public void insertFromFile_succeeds() throws TableNotFoundException, TableMalformedException,
DatabaseNotFoundException, ImageNotSupportedException, FileStorageException, IOException,
ArbitraryPrimaryKeysException, DataProcessingException {
create_table();
final TableInsertDto request = TableInsertDto.builder()
.delimiter(';')
.skipHeader(true)
.nullElement("NA")
.csv(new MockMultipartFile("weather-small", Files.readAllBytes(ResourceUtils.getFile("classpath:weather-small.csv").toPath())))
.build();
/* test */
dataService.insertFromFile(DATABASE_1_ID, TABLE_2_ID, request);
final Optional<Table> response = tableRepository.findByDatabaseAndId(DATABASE_1, TABLE_2_ID);
assertTrue(response.isPresent());
assertEquals(TABLE_2_ID, response.get().getId());
assertEquals(TABLE_2_NAME, response.get().getName());
assertEquals(TABLE_2_DESCRIPTION, response.get().getDescription());
}
@Test
@Disabled
public void insertFromFile_columnNumberDiffers_fails() throws DatabaseNotFoundException, ImageNotSupportedException,
IOException, ArbitraryPrimaryKeysException, DataProcessingException {
create_table();
final TableInsertDto request = TableInsertDto.builder()
.delimiter(';')
.skipHeader(true)
.nullElement("NA")
.csv(new MockMultipartFile("weather-small", Files.readAllBytes(ResourceUtils.getFile("classpath:namen.csv").toPath())))
.build();
/* test */
assertThrows(FileStorageException.class, () -> {
dataService.insertFromFile(DATABASE_1_ID, TABLE_2_ID, request);
});
}
@Test
public void insertFromFile_notRunning_fails() throws DatabaseNotFoundException, ImageNotSupportedException,
IOException, ArbitraryPrimaryKeysException, DataProcessingException {
create_table();
dockerClient.stopContainerCmd(request.getId()).exec();
final TableInsertDto request = TableInsertDto.builder()
.delimiter(';')
.skipHeader(true)
.nullElement("NA")
.csv(new MockMultipartFile("weather-small", Files.readAllBytes(ResourceUtils.getFile("classpath:weather-small.csv").toPath())))
.build();
/* test */
assertThrows(TableMalformedException.class, () -> {
dataService.insertFromFile(DATABASE_1_ID, TABLE_2_ID, request);
});
}
}
...@@ -151,61 +151,4 @@ public class TableServiceIntegrationTest extends BaseUnitTest { ...@@ -151,61 +151,4 @@ public class TableServiceIntegrationTest extends BaseUnitTest {
tableService.deleteTable(DATABASE_1_ID, TABLE_1_ID); tableService.deleteTable(DATABASE_1_ID, TABLE_1_ID);
} }
@Test
public void insertFromFile_succeeds() throws TableNotFoundException, TableMalformedException,
DatabaseNotFoundException, ImageNotSupportedException, FileStorageException, IOException,
ArbitraryPrimaryKeysException, DataProcessingException {
create_table_succeeds();
final TableInsertDto request = TableInsertDto.builder()
.delimiter(';')
.skipHeader(true)
.nullElement("NA")
.csv(new MockMultipartFile("weather-small", Files.readAllBytes(ResourceUtils.getFile("classpath:weather-small.csv").toPath())))
.build();
/* test */
tableService.insertFromFile(DATABASE_1_ID, TABLE_2_ID, request);
final Optional<Table> response = tableRepository.findByDatabaseAndId(DATABASE_1, TABLE_2_ID);
assertTrue(response.isPresent());
assertEquals(TABLE_2_ID, response.get().getId());
assertEquals(TABLE_2_NAME, response.get().getName());
assertEquals(TABLE_2_DESCRIPTION, response.get().getDescription());
}
@Test
@Disabled
public void insertFromFile_columnNumberDiffers_fails() throws DatabaseNotFoundException, ImageNotSupportedException,
IOException, ArbitraryPrimaryKeysException, DataProcessingException {
create_table_succeeds();
final TableInsertDto request = TableInsertDto.builder()
.delimiter(';')
.skipHeader(true)
.nullElement("NA")
.csv(new MockMultipartFile("weather-small", Files.readAllBytes(ResourceUtils.getFile("classpath:namen.csv").toPath())))
.build();
/* test */
assertThrows(FileStorageException.class, () -> {
tableService.insertFromFile(DATABASE_1_ID, TABLE_2_ID, request);
});
}
@Test
public void insertFromFile_notRunning_fails() throws DatabaseNotFoundException, ImageNotSupportedException,
IOException, ArbitraryPrimaryKeysException, DataProcessingException {
create_table_succeeds();
dockerClient.stopContainerCmd(request.getId()).exec();
final TableInsertDto request = TableInsertDto.builder()
.delimiter(';')
.skipHeader(true)
.nullElement("NA")
.csv(new MockMultipartFile("weather-small", Files.readAllBytes(ResourceUtils.getFile("classpath:weather-small.csv").toPath())))
.build();
/* test */
assertThrows(TableMalformedException.class, () -> {
tableService.insertFromFile(DATABASE_1_ID, TABLE_2_ID, request);
});
}
} }
package at.tuwien.service;
import at.tuwien.api.database.table.TableCreateDto;
import at.tuwien.api.database.table.TableCsvDto;
import at.tuwien.api.database.table.TableInsertDto;
import at.tuwien.entities.database.Database;
import at.tuwien.entities.database.table.Table;
import at.tuwien.entities.database.table.columns.TableColumn;
import at.tuwien.exception.*;
import at.tuwien.mapper.ImageMapper;
import at.tuwien.mapper.TableMapper;
import at.tuwien.repository.DatabaseRepository;
import at.tuwien.repository.TableRepository;
import com.opencsv.CSVParser;
import com.opencsv.CSVParserBuilder;
import com.opencsv.CSVReader;
import com.opencsv.CSVReaderBuilder;
import com.opencsv.exceptions.CsvException;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.EntityNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.sql.SQLException;
import java.util.*;
@Log4j2
@Service
public class ImportService extends JdbcConnector {
private final TableRepository tableRepository;
private final DatabaseRepository databaseRepository;
private final TableMapper tableMapper;
@Autowired
public ImportService(TableRepository tableRepository, DatabaseRepository databaseRepository,
ImageMapper imageMapper, TableMapper tableMapper) {
super(imageMapper, tableMapper);
this.tableRepository = tableRepository;
this.databaseRepository = databaseRepository;
this.tableMapper = tableMapper;
}
@Transactional
public Table findById(Long databaseId, Long tableId) throws TableNotFoundException, DatabaseNotFoundException {
final Optional<Table> table = tableRepository.findByDatabaseAndId(findDatabase(databaseId), tableId);
if (table.isEmpty()) {
log.error("table {} not found in database {}", tableId, databaseId);
throw new TableNotFoundException("table not found in database");
}
return table.get();
}
@Transactional
public Table createTable(Long databaseId, TableCreateDto createDto) throws ImageNotSupportedException,
DatabaseNotFoundException, DataProcessingException, ArbitraryPrimaryKeysException {
final Database database = findDatabase(databaseId);
/* create database in container */
try {
create(database, createDto);
} catch (SQLException e) {
throw new DataProcessingException("could not create table", e);
}
/* save in metadata db */
final Table mappedTable = tableMapper.tableCreateDtoToTable(createDto);
mappedTable.setDatabase(database);
mappedTable.setTdbid(databaseId);
mappedTable.setColumns(List.of()); // TODO: our metadata db model (primary keys x3) does not allow this currently
final Table table;
try {
table = tableRepository.save(mappedTable);
} catch (EntityNotFoundException e) {
log.error("failed to create table compound key: {}", e.getMessage());
throw new DataProcessingException("failed to create table compound key", e);
}
/* we cannot insert columns at the same time since they depend on the table id */
for (int i = 0; i < createDto.getColumns().length; i++) {
final TableColumn column = tableMapper.columnCreateDtoToTableColumn(createDto.getColumns()[i]);
column.setOrdinalPosition(i);
column.setCdbid(databaseId);
column.setTid(table.getId());
table.getColumns()
.add(column);
}
/* update table in metadata db */
try {
tableRepository.save(table);
} catch (EntityNotFoundException e) {
log.error("failed to create column compound key: {}", e.getMessage());
throw new DataProcessingException("failed to create column compound key", e);
}
log.info("Created table {}", table.getId());
log.debug("created table: {}", table);
return table;
}
/**
* TODO this needs to be at some different endpoint
* Insert data from a file into a table of a database with possible null values (denoted by a null element).
*
* @param databaseId The database.
* @param tableId The table.
* @param data The null element and delimiter.
* @throws TableNotFoundException The table does not exist in the metadata database.
* @throws ImageNotSupportedException The image is not supported.
* @throws DatabaseNotFoundException The database does not exist in the metdata database.
* @throws FileStorageException The CSV could not be parsed.
* @throws DataProcessingException The xml could not be mapped.
*/
@Transactional
public void insertFromFile(Long databaseId, Long tableId, TableInsertDto data)
throws TableNotFoundException, ImageNotSupportedException, DatabaseNotFoundException, FileStorageException,
TableMalformedException {
final Table table = findById(databaseId, tableId);
final TableCsvDto values;
try {
values = readCsv(data, table);
} catch (IOException | CsvException | ArrayIndexOutOfBoundsException e) {
log.error("failed to parse csv {}", e.getMessage());
throw new FileStorageException("failed to parse csv", e);
}
try {
insert(table, values);
} catch (SQLException | EntityNotSupportedException e) {
log.error("could not insert data {}", e.getMessage());
throw new TableMalformedException("could not insert data", e);
}
log.info("Inserted {} csv records into table id {}", values.getData().size(), tableId);
}
/* helper functions */
public Database findDatabase(Long id) throws DatabaseNotFoundException {
final Optional<Database> database = databaseRepository.findById(id);
if (database.isEmpty()) {
log.error("no database with this id found in metadata database");
throw new DatabaseNotFoundException("database not found in metadata database");
}
return database.get();
}
public TableCsvDto readCsv(TableInsertDto data, Table table) throws IOException, CsvException,
ArrayIndexOutOfBoundsException {
final CSVParser csvParser = new CSVParserBuilder()
.withSeparator(data.getDelimiter())
.build();
final Reader fileReader = new InputStreamReader(data.getCsv().getInputStream());
final List<List<String>> cells = new LinkedList<>();
final CSVReader reader = new CSVReaderBuilder(fileReader)
.withCSVParser(csvParser)
.withSkipLines(data.getSkipHeader() ? 1 : 0)
.build();
final List<Map<String, Object>> records = new LinkedList<>();
reader.readAll()
.forEach(x -> cells.add(Arrays.asList(x)));
/* map to the map-list structure */
for (List<String> row : cells) {
final Map<String, Object> record = new HashMap<>();
for (int i = 0; i < table.getColumns().size(); i++) {
record.put(table.getColumns().get(i).getInternalName(), row.get(i));
}
/* when the nullElement itself is null, nothing to do */
if (data.getNullElement() != null) {
record.replaceAll((key, value) -> value.equals(data.getNullElement()) ? null : value);
}
log.trace("processed {}", row);
records.add(record);
}
return TableCsvDto.builder()
.data(records)
.build();
}
}
...@@ -54,8 +54,6 @@ ...@@ -54,8 +54,6 @@
Create Table Create Table
</v-btn> </v-btn>
</v-card-actions> </v-card-actions>
</v-row>
</v-card-text>
</v-card> </v-card>
</div> </div>
</template> </template>
......
...@@ -73,17 +73,7 @@ export default { ...@@ -73,17 +73,7 @@ export default {
}, },
async createDB () { async createDB () {
this.loading = true this.loading = true
let res let res
// TODO list images and create image if needed
// try {
// res = await this.$axios.get('/api/container/api/image/')
// console.log(res.data)
// return
// } catch (e) {
// console.log(e)
// }
//
// create a container // create a container
let containerId let containerId
const isPublic = false const isPublic = false
...@@ -95,7 +85,7 @@ export default { ...@@ -95,7 +85,7 @@ export default {
tag: this.engine.tag tag: this.engine.tag
}) })
containerId = res.data.id containerId = res.data.id
console.log(containerId) console.debug('created container', res.data)
} catch (err) { } catch (err) {
this.$toast.error('Could not create container. Try another name.') this.$toast.error('Could not create container. Try another name.')
this.loading = false this.loading = false
...@@ -107,6 +97,7 @@ export default { ...@@ -107,6 +97,7 @@ export default {
res = await this.$axios.put(`/api/container/${containerId}`, { res = await this.$axios.put(`/api/container/${containerId}`, {
action: 'START' action: 'START'
}) })
console.debug('started container', res.data)
} catch (err) { } catch (err) {
this.loading = false this.loading = false
this.$toast.error('Could not start container.') this.$toast.error('Could not start container.')
......
...@@ -7,8 +7,13 @@ ...@@ -7,8 +7,13 @@
</v-stepper-step> </v-stepper-step>
<v-stepper-content class="pt-0 pb-1" step="1"> <v-stepper-content class="pt-0 pb-1" step="1">
<v-text-field v-model="tableName" required label="Name" /> <v-text-field
<v-text-field v-model="tableDesc" label="Description" /> v-model="tableCreate.name"
required
label="Name" />
<v-text-field
v-model="tableCreate.description"
label="Description" />
<v-btn :disabled="!step1Valid" color="primary" @click="step = 2"> <v-btn :disabled="!step1Valid" color="primary" @click="step = 2">
Continue Continue
</v-btn> </v-btn>
...@@ -19,6 +24,29 @@ ...@@ -19,6 +24,29 @@
</v-stepper-step> </v-stepper-step>
<v-stepper-content step="2"> <v-stepper-content step="2">
<v-row dense>
<v-col cols="8">
<v-checkbox
v-model="tableInsert.skipFirstRow"
label="Skip first row" />
</v-col>
</v-row>
<v-row dense>
<v-col cols="8">
<v-text-field
v-model="tableInsert.nullElement"
placeholder="e.g. NA or leave empty"
label="NULL Element" />
</v-col>
</v-row>
<v-row dense>
<v-col cols="8">
<v-text-field
v-model="tableInsert.delimiter"
label="Delimiter"
placeholder="e.g. ;" />
</v-col>
</v-row>
<v-row dense> <v-row dense>
<v-col cols="8"> <v-col cols="8">
<v-file-input <v-file-input
...@@ -27,8 +55,10 @@ ...@@ -27,8 +55,10 @@
show-size show-size
label="CSV File" /> label="CSV File" />
</v-col> </v-col>
<v-col cols="4" class="mt-3"> </v-row>
<v-btn :disabled="!file" :loading="loading" @click="upload">Upload</v-btn> <v-row dense>
<v-col cols="6">
<v-btn :disabled="!file" :loading="loading" color="primary" @click="upload">Next</v-btn>
</v-col> </v-col>
</v-row> </v-row>
</v-stepper-content> </v-stepper-content>
...@@ -37,7 +67,7 @@ ...@@ -37,7 +67,7 @@
Choose data type of columns Choose data type of columns
</v-stepper-step> </v-stepper-step>
<v-stepper-content step="3"> <v-stepper-content step="3">
<div v-for="(c, idx) in columns" :key="idx"> <div v-for="(c, idx) in tableCreate.columns" :key="idx">
<v-row dense class="column pa-2 ml-1 mr-1 mb-2"> <v-row dense class="column pa-2 ml-1 mr-1 mb-2">
<v-col cols="4"> <v-col cols="4">
<v-text-field v-model="c.name" disabled required label="Name" /> <v-text-field v-model="c.name" disabled required label="Name" />
...@@ -90,27 +120,35 @@ export default { ...@@ -90,27 +120,35 @@ export default {
data () { data () {
return { return {
step: 1, step: 1,
tableName: '', tableInsert: {
tableDesc: '', skipFirstRow: false,
nullElement: null,
delimiter: null
},
tableCreate: {
name: null,
description: null,
columns: []
},
loading: false, loading: false,
file: null, file: null,
fileLocation: null, fileLocation: null,
columns: [], columns: [],
columnTypes: [ columnTypes: [
{ value: 'ENUM', text: 'ENUM' }, { value: 'ENUM', text: 'Enumeration' },
{ value: 'BOOLEAN', text: 'BOOLEAN' }, { value: 'BOOLEAN', text: 'Boolean' },
{ value: 'NUMBER', text: 'NUMBER' }, { value: 'NUMBER', text: 'Number' },
{ value: 'BLOB', text: 'BLOB' }, { value: 'BLOB', text: 'Binary Large Object' },
{ value: 'DATE', text: 'DATE' }, { value: 'DATE', text: 'Date' },
{ value: 'STRING', text: 'STRING' }, { value: 'STRING', text: 'Varchar' },
{ value: 'TEXT', text: 'TEXT' } { value: 'TEXT', text: 'Text' }
], ],
newTableId: 42 newTableId: 42
} }
}, },
computed: { computed: {
step1Valid () { step1Valid () {
return this.tableName.length return this.tableName !== null
} }
}, },
mounted () { mounted () {
...@@ -126,37 +164,33 @@ export default { ...@@ -126,37 +164,33 @@ export default {
headers: { 'Content-Type': 'multipart/form-data' } headers: { 'Content-Type': 'multipart/form-data' }
}) })
if (res.data.success) { if (res.data.success) {
this.columns = res.data.columns this.tableCreate.columns = res.data.columns
this.fileLocation = res.data.file.filename this.fileLocation = res.data.file.filename
this.step = 3 this.step = 3
console.debug('upload csv', res.data)
} else { } else {
this.$toast.error('Could not upload CSV data') this.$toast.error('Could not upload CSV data')
return
} }
} catch (err) { } catch (err) {
this.$toast.error('Could not upload data.') this.$toast.error('Could not upload data.')
return
} }
this.loading = false this.loading = false
}, },
async createTable () { async createTable () {
const url = `/api/tables/api/database/${this.$route.params.db_id}/table/csv/local` const url = `/api/database/${this.$route.params.db_id}/table`
const data = {
columns: this.columns.map(c => c.type),
description: this.tableDesc,
name: this.tableName,
fileLocation: this.fileLocation
}
let res let res
try { try {
res = await this.$axios.post(url, data) res = await this.$axios.post(url, this.tableCreate)
this.newTableId = res.data.id this.newTableId = res.data.id
console.debug('created table', res.data)
} catch (err) { } catch (err) {
console.log(err) console.log(err)
return
} }
if (res && res.data && res.data.id) { // insert table
this.step = 4 this.step = 4
} else {
this.$toast.error('Could not create table.')
}
} }
} }
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment