diff --git a/build.gradle b/build.gradle index fa7807e..82f7120 100644 --- a/build.gradle +++ b/build.gradle @@ -36,7 +36,7 @@ repositories { dependencies { // dynapi - implementation 'org.dynapi:squirtle:0.5.9' + implementation 'org.dynapi:squirtle:0.5.10' implementation 'org.dynapi:openapispec:0.7.1' implementation 'org.dynapi:json-schema-gen:0.9.0' implementation 'org.dynapi:common:0.3.2' diff --git a/src/main/java/org/dynapi/dynapi/web/api/post/PostController.java b/src/main/java/org/dynapi/dynapi/web/api/post/PostController.java index 0e5e06b..43d38ea 100644 --- a/src/main/java/org/dynapi/dynapi/web/api/post/PostController.java +++ b/src/main/java/org/dynapi/dynapi/web/api/post/PostController.java @@ -1,29 +1,96 @@ package org.dynapi.dynapi.web.api.post; -import jakarta.servlet.http.HttpServletRequest; +import com.fasterxml.jackson.databind.JsonNode; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.dynapi.dynapi.core.database.interfaces.ColumnInformation; +import org.dynapi.dynapi.core.database.interfaces.Database; +import org.dynapi.squirtle.core.interfaces.SqlAble; +import org.dynapi.squirtle.core.queries.Schema; +import org.dynapi.squirtle.core.queries.Table; +import org.dynapi.squirtle.core.terms.parameters.QmarkParameter; +import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.server.ResponseStatusException; + +import java.util.*; @Slf4j @AllArgsConstructor @RestController @RequestMapping("/api") public class PostController { + private final Database database; + private final JdbcTemplate jdbcTemplate; + /** * adds one or more entries */ + @Transactional @PostMapping(value = "/{schema}/{table}", produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity postMultiple( - HttpServletRequest request, - @PathVariable String schema, - @PathVariable String table + @PathVariable("schema") String schemaName, + @PathVariable("table") String tableName, + @RequestBody JsonNode postBody ) { - return ResponseEntity.ok().build(); + List tableColumns = database.getColumnInformationOfTable(schemaName, tableName); + + Schema schema = new Schema(schemaName); + Table table = schema.table(tableName); + String sql = database.newQuery() + .into(table) + .columns(tableColumns.stream().map(col -> (SqlAble) table.field(col.getColumnName())).toList()) + .insert(tableColumns.stream().map(ignored -> new QmarkParameter()).toList()) + .getSql(); + log.info(sql); + + if (postBody.isObject()) { // insert single entry + Object[] values = getValues(tableColumns, postBody); + jdbcTemplate.update(sql, values); + } else if (postBody.isArray()) { // insert batch of entries + batchInsertCheckIfAllAreObject(postBody); + + // todo: improve with BatchPreparedStatementSetter instead of List + List batchValues = new ArrayList<>(); + for (int i = 0; i < postBody.size(); i++) { + JsonNode element = postBody.get(i); + Object[] values = getValues(tableColumns, element); + batchValues.add(values); + } + jdbcTemplate.batchUpdate(sql, batchValues); + } else { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Bad body. Has to be object or array"); + } + + return ResponseEntity.noContent().build(); + } + + protected void batchInsertCheckIfAllAreObject(JsonNode arrayNode) throws ResponseStatusException { + for (int i = 0; i < arrayNode.size(); i++) { + JsonNode element = arrayNode.get(i); + if (!element.isObject()) + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Batch element " + i + " is not an object"); + } + } + + protected Object[] getValues(List columns, JsonNode node) { + Object[] vals = new Object[columns.size()]; + for (int i = 0; i < columns.size(); i++) { + ColumnInformation column = columns.get(i); + String columnName = column.getColumnName(); + vals[i] = getValue(column, node.get(columnName)); + } + return vals; + } + + protected Object getValue(ColumnInformation columnInformation, JsonNode node) { + if (node.isMissingNode() || node.isNull()) + return null; + // todo: requires ColumnInformation#getSimplifiedType + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Bad body. can't convert field"); } }