Search in sources :

Example 1 with KeyValuePair

use of com.bakdata.quick.common.api.model.KeyValuePair in project quick by bakdata.

the class IngestController method convertIngestData.

/**
 * Processes raw input data for ingesting.
 *
 * <p>
 * Two steps are necessary to ingest the raw payload:
 * <ul>
 *    <li> parse it
 *    <li> check for existing keys if the topic is immutable
 * </ul>
 *
 * <p>
 * This method handles both steps and then forwards all non-existing {@link KeyValuePair} to the
 * {@link IngestService}.
 * Further, it handles merging the error messages. The method returns an error for existing keys as well as possible
 * errors in the ingest service.
 *
 * @param topic   the topic to ingest to
 * @param payload the raw payload
 * @param data    the topic information for the topic
 * @param <K>     the key type of the topic
 * @param <V>     the value type of the topic
 * @return merged completable possibly containing errors from existing keys or the ingest service
 */
private <K, V> Completable convertIngestData(final String topic, final String payload, final QuickTopicData<K, V> data) {
    final Single<List<KeyValuePair<K, V>>> list = Single.fromCallable(() -> this.parser.parseInputData(payload, data));
    return list.flatMap(pairs -> this.filter.prepareIngest(data, pairs)).flatMapCompletable(pairs -> {
        final Completable existingError = createErrorsForExistingKeys(topic, pairs);
        final Completable ingest = this.ingestService.sendData(topic, pairs.getDataToIngest());
        return Completable.mergeArrayDelayError(existingError, ingest);
    });
}
Also used : Controller(io.micronaut.http.annotation.Controller) Secured(io.micronaut.security.annotation.Secured) TopicTypeService(com.bakdata.quick.common.type.TopicTypeService) QuickTopicData(com.bakdata.quick.common.type.QuickTopicData) Completable(io.reactivex.Completable) IngestFilter(com.bakdata.quick.ingest.service.IngestFilter) Delete(io.micronaut.http.annotation.Delete) SecurityRule(io.micronaut.security.rules.SecurityRule) BadArgumentException(com.bakdata.quick.common.exception.BadArgumentException) Single(io.reactivex.Single) Collectors(java.util.stream.Collectors) Post(io.micronaut.http.annotation.Post) Inject(javax.inject.Inject) IngestParser(com.bakdata.quick.ingest.service.IngestParser) IngestService(com.bakdata.quick.ingest.service.IngestService) List(java.util.List) Slf4j(lombok.extern.slf4j.Slf4j) Body(io.micronaut.http.annotation.Body) KeyValuePair(com.bakdata.quick.common.api.model.KeyValuePair) IngestLists(com.bakdata.quick.ingest.service.IngestFilter.IngestLists) Completable(io.reactivex.Completable) List(java.util.List)

Example 2 with KeyValuePair

use of com.bakdata.quick.common.api.model.KeyValuePair in project quick by bakdata.

the class IngestParser method parseInputData.

/**
 * Parses an object or array of key value pairs into a list.
 *
 * @param payload   the raw key value pairs as json, either a single one or an array
 * @param topicData the registry's topicData about the topic
 * @param <K>       type of the key
 * @param <V>       type of the value
 * @return list of parsed key value pairs
 * @throws IOException Jackson JSON error
 */
public <K, V> List<KeyValuePair<K, V>> parseInputData(final String payload, final QuickTopicData<K, V> topicData) throws IOException {
    final JsonNode jsonNode;
    try (final JsonParser parser = this.objectMapper.getFactory().createParser(payload)) {
        jsonNode = parser.readValueAsTree();
        if (jsonNode.isObject()) {
            return List.of(fromJsonNode(jsonNode, topicData));
        }
        if (jsonNode.isArray()) {
            final List<KeyValuePair<K, V>> pairs = new ArrayList<>();
            final Iterator<JsonNode> elements = jsonNode.elements();
            while (elements.hasNext()) {
                pairs.add(fromJsonNode(elements.next(), topicData));
            }
            return pairs;
        }
        throw new BadArgumentException("Expected key-value object or list of key-value objects. Got: " + jsonNode.getNodeType());
    }
}
Also used : BadArgumentException(com.bakdata.quick.common.exception.BadArgumentException) KeyValuePair(com.bakdata.quick.common.api.model.KeyValuePair) ArrayList(java.util.ArrayList) JsonNode(com.fasterxml.jackson.databind.JsonNode) JsonParser(com.fasterxml.jackson.core.JsonParser)

Example 3 with KeyValuePair

use of com.bakdata.quick.common.api.model.KeyValuePair in project quick by bakdata.

the class IngestParser method fromJsonNode.

/**
 * Parses a single key value object.
 *
 * @param jsonNode node to parse
 * @param data     the registry's data about the topic
 * @param <K>      type of the key
 * @param <V>      type of the value
 * @return parsed key value pair
 */
private static <K, V> KeyValuePair<K, V> fromJsonNode(final JsonNode jsonNode, final QuickTopicData<K, V> data) {
    final JsonNode key = jsonNode.get("key");
    final JsonNode value = jsonNode.get("value");
    if (key == null || value == null) {
        throw new BadArgumentException(String.format("Could not find 'key' or 'value' fields in: %s", jsonNode));
    }
    final TypeResolver<K> keyResolver = data.getKeyData().getResolver();
    final TypeResolver<V> valueResolver = data.getValueData().getResolver();
    return new KeyValuePair<>(parse(keyResolver, key), parse(valueResolver, value));
}
Also used : BadArgumentException(com.bakdata.quick.common.exception.BadArgumentException) KeyValuePair(com.bakdata.quick.common.api.model.KeyValuePair) JsonNode(com.fasterxml.jackson.databind.JsonNode)

Example 4 with KeyValuePair

use of com.bakdata.quick.common.api.model.KeyValuePair in project quick by bakdata.

the class IngestController method createErrorsForExistingKeys.

/**
 * Converts already existing keys into an error.
 *
 * @param topic the topic to ingest into
 * @param pairs lists of existing and non existing keys respectively
 * @return successful completable if there are no existing keys otherwise an error
 */
private static Completable createErrorsForExistingKeys(final String topic, final IngestLists<?, ?> pairs) {
    if (pairs.getExistingData().isEmpty()) {
        return Completable.complete();
    } else {
        final String existingKeys = pairs.getExistingData().stream().map(KeyValuePair::getKey).map(Object::toString).collect(Collectors.joining(", "));
        final String errorMessage = String.format("The following keys already exist for immutable topic %s: %s", topic, existingKeys);
        return Completable.error(new BadArgumentException(errorMessage));
    }
}
Also used : BadArgumentException(com.bakdata.quick.common.exception.BadArgumentException) KeyValuePair(com.bakdata.quick.common.api.model.KeyValuePair)

Aggregations

KeyValuePair (com.bakdata.quick.common.api.model.KeyValuePair)4 BadArgumentException (com.bakdata.quick.common.exception.BadArgumentException)4 JsonNode (com.fasterxml.jackson.databind.JsonNode)2 QuickTopicData (com.bakdata.quick.common.type.QuickTopicData)1 TopicTypeService (com.bakdata.quick.common.type.TopicTypeService)1 IngestFilter (com.bakdata.quick.ingest.service.IngestFilter)1 IngestLists (com.bakdata.quick.ingest.service.IngestFilter.IngestLists)1 IngestParser (com.bakdata.quick.ingest.service.IngestParser)1 IngestService (com.bakdata.quick.ingest.service.IngestService)1 JsonParser (com.fasterxml.jackson.core.JsonParser)1 Body (io.micronaut.http.annotation.Body)1 Controller (io.micronaut.http.annotation.Controller)1 Delete (io.micronaut.http.annotation.Delete)1 Post (io.micronaut.http.annotation.Post)1 Secured (io.micronaut.security.annotation.Secured)1 SecurityRule (io.micronaut.security.rules.SecurityRule)1 Completable (io.reactivex.Completable)1 Single (io.reactivex.Single)1 ArrayList (java.util.ArrayList)1 List (java.util.List)1