All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.restheart.exchange.BsonFromCsvRequest Maven / Gradle / Ivy

There is a newer version: 8.1.4
Show newest version
/*-
 * ========================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);
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy