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

io.kestra.plugin.pulsar.GenericRecordProducer Maven / Gradle / Ivy

There is a newer version: 0.18.0
Show newest version
package io.kestra.plugin.pulsar;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.pulsar.client.api.*;
import org.apache.pulsar.client.api.schema.*;
import org.apache.pulsar.client.impl.schema.generic.*;

import io.kestra.core.runners.RunContext;

public class GenericRecordProducer extends AbstractProducer {
    
    private Schema schema;

    private final String schemaString;
    private final SchemaType schemaType;
    
    public GenericRecordProducer(RunContext runContext, PulsarClient client, String schemaString, SchemaType schemaType) {
        super(runContext, client);
        
        this.schemaString = schemaString;
        this.schemaType =  schemaType;
    }
    
    @Override
    protected ProducerBuilder getProducerBuilder(PulsarClient client) {
        if (this.schemaString == null) {
            throw new IllegalArgumentException("Must pass a \"schemaString\" when the \"schemaType\" is not null");
        }
        
        SchemaDefinition schemaDef = SchemaDefinition
            .builder()
            .withJsonDef(this.schemaString)
            .build();
        
        this.schema = this.schemaType == SchemaType.AVRO ? Schema.AVRO(schemaDef) : Schema.JSON(schemaDef);
        return client.newProducer(Schema.generic(schema.getSchemaInfo()));
    }
    
    @SuppressWarnings("unchecked")
    @Override
    protected TypedMessageBuilder createMessageWithValue(Map renderedMap) throws Exception {
        this.producer = this.producerBuilder.create();
        TypedMessageBuilder message = producer.newMessage();

        if (renderedMap.containsKey("value")) {
          // For AVRO schemas we need to load the native schema to check if we need to denest any records types
          org.apache.pulsar.shade.org.apache.avro.Schema nativeSchema = this.schemaType == SchemaType.AVRO ? ((org.apache.pulsar.shade.org.apache.avro.Schema)this.schema.getNativeSchema().get()) : null;

          
          GenericSchemaImpl schema = this.schemaType == SchemaType.AVRO ? GenericAvroSchema.of(this.schema.getSchemaInfo()) : GenericJsonSchema.of(this.schema.getSchemaInfo());
          GenericRecordBuilder record = schema.newRecordBuilder();
            Map value = (Map)renderedMap.get("value");
            value.forEach((k,v) -> record.set(k, this.schemaType == SchemaType.AVRO ? denestRecord(v, nativeSchema.getField(k).schema()): v));
            message.value(record.build());
        }

        return message;
    }
    
    @SuppressWarnings("unchecked")
    private Object denestRecord(Object value, org.apache.pulsar.shade.org.apache.avro.Schema schema) {      
      switch(schema.getType()) {
        case RECORD:
          // This doesn't work for user-defined class objects. However, given values are inputs from Kestra they should all be maps
          org.apache.pulsar.shade.org.apache.avro.generic.GenericRecord record = new org.apache.pulsar.shade.org.apache.avro.generic.GenericData.Record(schema);
          for (Map.Entry entry : ((Map)value).entrySet())
            record.put(entry.getKey(), denestRecord(entry.getValue(), schema.getField(entry.getKey()).schema()));
          return record;
        case ARRAY:
          List list = new ArrayList();
          for (Object x : (Iterable)value) {
            list.add(denestRecord(x, schema.getElementType()));
          }
          return list;
        case MAP:
          HashMap map = new HashMap<>();
          for (Map.Entry entry : ((Map)value).entrySet())
            map.put(entry.getKey(), denestRecord(entry.getValue(), schema.getValueType()));
          return map;
        default:
          return value;
      }
    }
}