avro json additional field - avro

I have following avro schema
{
"type":"record",
"name":"test",
"namespace":"test.name",
"fields":[
{"name":"items","type":
{"type":"array",
"items":
{"type":"record","name":"items",
"fields":[
{"name":"name","type":"string"},
{"name":"state","type":"string"}
]
}
}
},
{"name":"firstname","type":"string"}
]
}
when I am using Json decoder and avro encoder to encode Json data:
val writer = new GenericDatumWriter[GenericRecord](schema)
val reader = new GenericDatumReader[GenericRecord](schema)
val baos = new ByteArrayOutputStream
val decoder: JsonDecoder = DecoderFactory.get.jsonDecoder(schema, json)
val encoder = EncoderFactory.get.binaryEncoder(baos, null)
val datum = reader.read(null, decoder)
writer.write(datum, encoder)
encoder.flush()
val avroByteArray = baos.toByteArray
scenario1:
when I am passing following json to encode it works fine:
{
"items": [
{
"name": "dallas",
"state": "TX"
}
],
"firstname":"arun"
}
scenario2:
when I am passing additional attribute in json at root level (lastname) it is able to encode and works fine:
{
"items": [
{
"name": "dallas",
"state": "TX"
}
],
"firstname":"fname",
"lastname":"lname"
}
scenario3:
when I am add additional attribute in array record (country) it is throwing following exception:
Expected record-end. Got FIELD_NAME
org.apache.avro.AvroTypeException: Expected record-end. Got FIELD_NAME
at org.apache.avro.io.JsonDecoder.error(JsonDecoder.java:698)
{
"items": [
{
"name": "dallas",
"state": "TX",
"country":"USA"
}
],
"firstname":"fname",
"lastname":"lname"
}
I need to make scenario#3 working any help will be great.

Covert the json data with respective avro schema format using spark data frame approach would help you.
create struct type from avro schema using
SchemaConverters
create data frame from struct type and json rdd string step
convert the data frame rows into json using
df.toJSON
Sample test cases:
import java.io.ByteArrayOutputStream
import com.databricks.spark.avro.SchemaConverters
import org.apache.avro.Schema
import org.apache.avro.Schema.Parser
import org.apache.avro.generic._
import org.apache.avro.io._
import org.apache.spark.SparkContext
import org.apache.spark.rdd.RDD
import org.apache.spark.sql.SparkSession.Builder
import org.apache.spark.sql._
import org.apache.spark.sql.types.StructType
import org.scalatest.{Matchers, WordSpecLike}
class Test extends WordSpecLike
with Matchers {
val schemaString: String =
"""{
| "type":"record",
| "name":"test",
| "namespace":"test.name",
| "fields":[
| {"name":"items","type":
| {"type":"array",
| "items":
| {"type":"record","name":"items",
| "fields":[
| {"name":"name","type":"string"},
| {"name":"state","type":"string"}
| ]
| }
| }
| },
| {"name":"firstname","type":"string"}
| ]
|}""".stripMargin
// create spark session and sql context
val builder: Builder = SparkSession.builder.appName("testAvroSpark")
val sparkSession: SparkSession = builder.master("local[1]").getOrCreate()
val sc: SparkContext = sparkSession.sparkContext
val sqlContext: SQLContext = sparkSession.sqlContext
// avro schema from json type schema string
val schema: Schema = new Parser().parse(schemaString)
// get spark struct type from avro schema
val requiredType: StructType =
SchemaConverters.toSqlType(schema).dataType.asInstanceOf[StructType]
"scenario one json data with given schema" in {
val scenarioOneJson: String =
"""{
| "items": [
| {
| "name": "dallas",
| "state": "TX"
| }
| ],
| "firstname":"rumesh"
|}""".stripMargin
val jsonRdd: RDD[String] = sc.parallelize(Seq(scenarioOneJson))
val outputJsonExpected: String =
"""{"items":[{"name":"dallas","state":"TX"}],"firstname":"rumesh"}"""
val resultJson: String = customJsonConverter(requiredType, jsonRdd).head
assert(resultJson === outputJsonExpected)
assert(binaryEncoder(schema, outputJsonExpected) === binaryEncoder(schema, resultJson))
}
"scenario two json data with given schema" in {
val scenarioTwoJson: String =
"""{
| "items": [
| {
| "name": "dallas",
| "state": "TX"
| }
| ],
| "firstname":"rumesh",
| "lastname":"krish"
|}""".stripMargin
val jsonRdd: RDD[String] = sc.parallelize(Seq(scenarioTwoJson))
val outputJsonExpected: String =
"""{"items":[{"name":"dallas","state":"TX"}],"firstname":"rumesh"}"""
val resultJson: String = customJsonConverter(requiredType, jsonRdd).head
assert(resultJson === outputJsonExpected)
assert(binaryEncoder(schema, outputJsonExpected) === binaryEncoder(schema, resultJson))
}
"scenario three json data with given schema" in {
val scenarioThreeJson: String =
"""{
| "items": [
| {
| "name": "dallas",
| "state": "TX",
| "country":"USA"
| }
| ],
| "firstname":"rumesh",
| "lastname":"krish"
|}""".stripMargin
val jsonRdd: RDD[String] = sc.parallelize(Seq(scenarioThreeJson))
val outputJsonExpected: String =
"""{"items":[{"name":"dallas","state":"TX"}],"firstname":"rumesh"}"""
val resultJson: String = customJsonConverter(requiredType, jsonRdd).head
assert(resultJson === outputJsonExpected)
assert(binaryEncoder(schema, outputJsonExpected) === binaryEncoder(schema, resultJson))
}
/**
* convert the json using data frame json parser with given schema struct type
*
* #param customType given data frame struct type
* #param jsonInputRdd json rdd string
* #return
*/
private def customJsonConverter(customType: StructType,
jsonInputRdd: RDD[String]): List[String] = {
// create data frame from rdd string with struct type schema
val df: DataFrame = sqlContext.read.schema(customType).json(jsonInputRdd)
// get the list of json string data frame
df.toJSON.rdd.toLocalIterator.toList
}
/**
* avro binary serialization
*
* #param avroSchema avro schema
* #param jsonData json data
* #return
*/
private def binaryEncoder(avroSchema: Schema, jsonData: String): Array[Byte] = {
val writer = new GenericDatumWriter[GenericRecord](avroSchema)
val reader = new GenericDatumReader[GenericRecord](avroSchema)
val baos = new ByteArrayOutputStream
val decoder: JsonDecoder = DecoderFactory.get.jsonDecoder(avroSchema, jsonData)
val encoder = EncoderFactory.get.binaryEncoder(baos, null)
val datum = reader.read(null, decoder)
writer.write(datum, encoder)
encoder.flush()
baos.toByteArray
}
}

Your schema does not represent the structure in scenario 3: the 'country' field is missing:
{"name":"country", "type":"string"}
You are only declaring the fields 'name' and 'state'. Then decoder correctly expects the (sub)record to end after those and, as the error message states, it gets a(nother) field name instead ('country').
Btw: You could use a generator to always get a matching schema out of your JSON, there are a couple available in the net.

Related

How to maintain data type in respond after connecting with Datastax Astra DB?

I am using Datastax Astra database. I am uploading csv file and I am setting all the columns data type as float, as per column. I connected with my db via python http_method.
res = astra_client.request(
method=http_methods.GET,
path=f"/api/rest/v2/keyspaces/{ASTRA_DB_KEYSPACE}/{ASTRA_DB_COLLECTION}/rows")
This gives me all the rows, but the data type is not maintained in the respond. All the float converted to string. Why and How can I solve this ?
{'PetalWidthCm': '0.2', 'Id': 23, 'PetalLengthCm': '1.0', 'SepalWidthCm': '3.6', 'Species': 'Iris-setosa', 'SepalLengthCm': '4.6'}
How are you creating your table and adding data? For example, the following works for me.
import http.client
import json
ASTRA_TOKEN = ""
ASTRA_DB_ID = ""
ASTRA_DB_REGION = ""
ASTRA_DB_KEYSPACE = "testks"
ASTRA_DB_COLLECTION = "float_test"
conn = http.client.HTTPSConnection(f"{ASTRA_DB_ID}-{ASTRA_DB_REGION}.apps.astra.datastax.com")
headers = {
'X-Cassandra-Token': ASTRA_TOKEN,
'Content-Type': 'application/json'
}
# Create the table
createTablePayload = json.dumps({
"name": f"{ASTRA_DB_COLLECTION}",
"columnDefinitions": [
{"name": "id", "typeDefinition": "text"},
{"name": "floatval", "typeDefinition": "float"},
{"name": "intval", "typeDefinition": "int"},
{"name": "doubleval", "typeDefinition": "double"}
],
"primaryKey": {"partitionKey": ["id"]},
"ifNotExists": True
})
# Add some data
conn.request("POST", f"/api/rest/v2/schemas/keyspaces/{ASTRA_DB_KEYSPACE}/tables", createTablePayload, headers)
res = conn.getresponse()
data = res.read()
print(data.decode("utf-8"))
addRowPayload = json.dumps({
"id": "af2603d2-8c03-11eb-a03f-0ada685e0000",
"floatval": 1.1,
"intval": 2,
"doubleval": 3.3
})
conn.request("POST", f"/api/rest/v2/keyspaces/{ASTRA_DB_KEYSPACE}/{ASTRA_DB_COLLECTION}", addRowPayload, headers)
res = conn.getresponse()
data = res.read()
print(data.decode("utf-8"))
# Read back the data
conn.request("GET", f"/api/rest/v2/keyspaces/{ASTRA_DB_KEYSPACE}/{ASTRA_DB_COLLECTION}/rows", "", headers)
res = conn.getresponse()
data = res.read()
print(data.decode("utf-8"))
The response from this is
$ python3 get_rows.py
{"name":"float_test"}
{"id":"af2603d2-8c03-11eb-a03f-0ada685e0000"}
{"count":1,"data":[{"id":"af2603d2-8c03-11eb-a03f-0ada685e0000","doubleval":3.3,"intval":2,"floatval":1.1}]}

Gatling: How to pass jsonPath saved variable to another exec

I am new to Gatling and scala. facing issue on passing jsonpath saved variable from one repeat section to another forEach repeat section.
following variable "dcIds" is not able to pass to forEach section. Also please direct me to make the below code more better.
var dcIdd = ""
val r = new scala.util.Random
def orderRef() = r.nextInt(100)
def getCreateRequest: String = {
val data = s"""
[{
"name":"DC_${orderRef()}",
"location":"Seattle, Washington, USA",
"type":"Colocation"
}]
""".stripMargin
data
}
def createAppRequest: String = {
val data = s"""
[{
"name":"App_${orderRef()}",
"owner":"a#a.com",
"dataCenterId":"${dcIdd}",
"strategy":"Rehost",
"migrationStrategy":"Rehost"}]
}]
""".stripMargin
data
}
val scn = scenario("Add DC")
.repeat(DcIterations, "index") {
exec(
http("List_plans")
.get(uri2 + "?plan_id=")
.headers(headers_sec)
.resources(
http("DC add")
.post(uri2)
.headers(headers_sec)
.body(StringBody(session => getCreateRequest))
.check(jsonPath("$.ids[*]").findAll.saveAs("dcIds"))))
}
.foreach("${dcIds}", "dcId") {
dcIdd = "${dcId}"
repeat(AppIterations, "index") {
exec(http("Add Application")
.post(uri1 + "/applications/${dcId}")
.headers(headers_sec)
.body(StringBody(session => createAppRequest))
)
}
}

Metric math alarms: How can I use a for_each expression to loop over metrics within a dynamic block?

I am trying to create dynamic metric math alarms, that are configurable with a JSON.
I am struggling with looping over the metric alarm with a for_each expression as this is a loop within a loop.
Here is an example of what I am trying to do:
resource "aws_cloudwatch_metric_alarm" "Percentage_Alert" {
for_each = var.percentage_error_details
locals { alarm_details = each.value }
alarm_name = "${terraform.workspace}-${each.key}"
comparison_operator = local.alarm_details["Comparison_Operator"]
evaluation_periods = "1"
threshold = local.alarm_details["Threshold"]
metric_query {
id = "e1"
expression = local.alarm_details["Expression"]
label = local.alarm_details["Label"]
return_data = "true"
}
dynamic "metric_query" {
for metric in each.value["Metrics"]{
id = metric.key
metric_name = metric.value
period = local.alarm_details["Period"]
stat = local.alarm_details["Statistic"]
namespace = local.full_namespace
unit = "Count"
}
}
}
And this is the sample JSON
{
"locals": {
"Name": {
"Name": "metric_math",
"Metrics": {
"m1": "Sucess",
"m2": "Failure"
},
"Expression": "100*(m2/(m1+m2))",
"Threshold" : 1,
"Period": 25,
"Priority": "critical",
"Statistic": "Sum",
"Label": "label",
"Comparison_Operator": "GreaterThanOrEqualToThreshold"
}
}
}
And this is the error message i'm getting:
Error: Invalid block definition
On ../modules/cloudwatch/metriclogfilter/main.tf line 89: Either a quoted
string block label or an opening brace ("{") is expected here.
Any help would be much appreciated.

how to set custom example in #ApiResponse in swagger [duplicate]

I am facing issue with example in response.
#ApiResponse(code=200,
message="fetch list of Service/Config Resources",
response = testing.class,
responseContainer = "List",
examples=#Example(
value = #ExampleProperty(
mediaType = MediaType.APPLICATION_JSON_VALUE,
value = "{testingId: 1234, testingName = Testing Name}"
)
)
)
But getting response example as
[
{
"testingId": "string",
"testingName": "string"
}
]

Dynamic cell color in jspdf autoTable?

Is it possible to define the cell color with a nested property of the object mapped to the table ?
The JSON structure of the objects is :
objects: [
{
"agent": "agent_1",
"days": {
day_0: {
"code": "ABC",
"color": "#0062cc"
},
day_1: {
"code": "DEF",
"color": "#a09494b2"
}
},
{
[...]
}
]
I have a table defined like this :
let columns = [
{title: "Agent", dataKey: "agent"},
{title: "january 1st", dataKey: "day_0"},
{title: "january 2nd", dataKey: "day_1"}]
let rows = [
{agent: "agent_1", day_0: "ABC", day_1: "DEF"},
[...]
]
All that works fine. But I'd like to set the color of each day cell dynamically, set with the color code of the corresponding object. Something like :
createdCell: function(cell, data) {
{
cell.styles.fillColor = "day_0.color";
}
}
But I can't figure how to pass the data to the table. Is it possible ? Can displayProperty help in any way ?
EDIT: In this case it was that v2.3.4 of jspdf-autotable was needed
Based on our comments discussion I think I understood your problem. You can try something like this (with the hexToRgb function from here)
let columns = [{
title: "Agent",
dataKey: "agent"
},
{
title: "january 1st",
dataKey: "day_0"
},
{
title: "january 2nd",
dataKey: "day_1"
}
]
let objects = [{
agent: "agent_1",
day_0: {
"code": "ABC",
"color": "#00ff00"
},
day_1: {
"code": "DEF",
"color": "#ff0000"
}
// etc
}];
let doc = jsPDF()
doc.autoTable(columns, objects, {
createdCell: function(cell, data) {
let hex = cell.raw.color
if (hex) {
let rgb = hexToRgb(hex)
cell.styles.fillColor = rgb;
cell.text = cell.raw.code
}
}
});
doc.save('jhg.pdf')
function hexToRgb(hex) {
var bigint = parseInt(hex.replace('#', ''), 16);
var r = (bigint >> 16) & 255;
var g = (bigint >> 8) & 255;
var b = bigint & 255;
return [r, g, b];
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.4.1/jspdf.debug.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf-autotable/2.3.4/jspdf.plugin.autotable.js"></script>
I just leave the answer here just in case anyone needs it.
We can use the didParseCell hook.
doc.autoTable({
head: [[..., color]],
body: [[..., #ffffff], [..., #ff0000]], // pass hexa value to the cell
didParseCell: function (HookData) {
if (HookData.cell == undefined)
return;
// find cell taht contains the hexa value
// the change the fillColor property
// and set the cell value to empty
var color = HookData.cell.text[0];
if (color.match(/^#[a-fA-F0-9]{3}([a-fA-F0-9]{3})/g) != null) {
HookData.cell.styles.fillColor = hexToRgb(color);
HookData.cell.text = [];
}
}
});
Code to convert hexa to RGB:
hexToRgb(hex) {
var bigint = parseInt(hex.replace('#', ''), 16);
var r = (bigint >> 16) & 255;
var g = (bigint >> 8) & 255;
var b = bigint & 255;
return [r, g, b];
}
Package version:
jspdf : 2.5.1
jspdf-autotable :3.5.25

Resources