org.restheart.exchange.BsonFromCsvRequest Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of restheart-commons Show documentation
Show all versions of restheart-commons Show documentation
RESTHeart Commons - Common classes for core components and plugins.
/*-
* ========================LICENSE_START=================================
* restheart-security
* %%
* Copyright (C) 2018 - 2024 SoftInstigate
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* =========================LICENSE_END==================================
*/
package org.restheart.exchange;
import java.io.IOException;
import java.util.Arrays;
import java.util.Deque;
import java.util.List;
import java.util.Scanner;
import org.bson.BsonArray;
import org.bson.BsonString;
import org.bson.BsonValue;
import org.bson.json.JsonParseException;
import org.bson.types.ObjectId;
import org.restheart.utils.BsonUtils;
import static org.restheart.utils.BsonUtils.document;
import org.restheart.utils.ChannelReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.undertow.server.HttpServerExchange;
import io.undertow.util.HeaderValues;
import io.undertow.util.Headers;
/**
* ServiceRequest implementation backed by BsonValue and initialized from csv
* data. Two query parameters controls the conversion: 'id', the the index of
* the _id property and 'sep', the separator char
*
* @author Andrea Di Cesare {@literal }
*/
public class BsonFromCsvRequest extends ServiceRequest {
private static final Logger LOGGER = LoggerFactory.getLogger(BsonFromCsvRequest.class);
/**
*
*/
public static final String CVS_CONTENT_TYPE = "text/csv";
protected BsonFromCsvRequest(HttpServerExchange exchange) {
super(exchange);
}
public static BsonFromCsvRequest init(HttpServerExchange exchange) {
var ret = new BsonFromCsvRequest(exchange);
if (!checkContentType(exchange)) {
LOGGER.warn("error initializing request, " + "Contenty-Type is not {}", CVS_CONTENT_TYPE);
ret.setInError(true);
}
return ret;
}
public static BsonFromCsvRequest of(HttpServerExchange exchange) {
return of(exchange, BsonFromCsvRequest.class);
}
@Override
public BsonArray parseContent() throws IOException, BadRequestException {
final var params = new CsvRequestParams(wrapped);
final var csv = ChannelReader.readString(wrapped);
return parseCsv(params, csv);
}
private static boolean checkContentType(HttpServerExchange exchange) {
HeaderValues contentType = exchange.getRequestHeaders().get(Headers.CONTENT_TYPE);
return contentType != null && contentType.stream()
.anyMatch(ct -> ct.equals(CVS_CONTENT_TYPE) || ct.startsWith(CVS_CONTENT_TYPE.concat(";")));
}
private BsonArray parseCsv(CsvRequestParams params, String csv) throws IOException {
var bson = new BsonArray();
boolean isHeader = true;
List cols = null;
try (Scanner scanner = new Scanner(csv)) {
while (scanner.hasNext()) {
String line = scanner.nextLine();
// split on the separator only if that comma has zero,
// or an even number of quotes ahead of it.
List vals = Arrays.asList(line.split(params.sep + "(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)", -1));
if (isHeader) {
cols = vals;
} else {
var doc = document().put("_etag", new ObjectId()).get();
int unnamedProps = 0;
for (int idx = 0; idx < vals.size(); idx++) {
if (idx == params.idIdx) {
var _v = vals.get(params.idIdx);
if (_v != null) {
// quote empty string
if ("".equals(_v.strip())) {
_v = "\"".concat(_v).concat("\"");
}
doc.append("_id", getBsonValue(_v));
}
} else {
String propname;
if (cols == null || cols.size() <= idx) {
propname = "unnamed_" + unnamedProps;
unnamedProps++;
} else {
propname = cols.get(idx);
}
var _v = vals.get(idx);
// quote empty string
if ("".equals(_v.strip())) {
_v = "\"".concat(_v).concat("\"");
}
if (_v != null) {
doc.append(propname, getBsonValue(_v));
}
}
}
bson.add(doc);
}
isHeader = false;
}
}
return bson;
}
private BsonValue getBsonValue(String raw) {
try {
return BsonUtils.parse(raw);
} catch (JsonParseException jpe) {
return new BsonString(raw);
}
}
private static class CsvRequestParams {
private static final String ID_IDX_QPARAM_NAME = "id";
private static final String SEPARATOR_QPARAM_NAME = "sep";
public final int idIdx;
public final String sep;
CsvRequestParams(HttpServerExchange exchange) {
Deque _sep = exchange.getQueryParameters().get(SEPARATOR_QPARAM_NAME);
Deque _id = exchange.getQueryParameters().get(ID_IDX_QPARAM_NAME);
sep = _sep != null ? !_sep.isEmpty() ? _sep.getFirst() : "" : ",";
String _idIdx = _id != null ? !_id.isEmpty() ? _id.getFirst() : "-1" : "-1";
try {
idIdx = Integer.parseInt(_idIdx);
} catch (NumberFormatException nfe) {
throw new IllegalArgumentException(nfe);
}
}
}
}