Store single json from azure iot hub to datalake2 - azure-data-lake

I added iot hub and devices. All data from iot hub is saved to data lake 2 in json format. Works fine but if there are several messages at once from device, it is saved in a single json. It causes some troubles... Is there a way to save each message-event in a separate json? I've looked through settings of iot hub but found nothing.

There is no such as settings for always forwarding a single message to the storage in the IoT Hub routing mechanism. Basically this requirement can be implemented by azure function either in the stream pipeline consumer (IoTHubTrigger) or in the event grid subscriber (EventGridTrigger).
Update:
The following is an example of the IoTHubTrigger function with an output blob binding to the container of the Data Lake Storage Gen2:
run.csx:
#r "Microsoft.Azure.EventHubs"
#r "Newtonsoft.Json"
#r "Microsoft.WindowsAzure.Storage"
using System;
using System.IO;
using System.Text;
using System.Linq;
using Microsoft.Azure.EventHubs;
using Microsoft.WindowsAzure.Storage.Blob;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
public static async Task Run(EventData ed, CloudBlockBlob outputBlob, ILogger log)
{
//log.LogInformation($"DeviceId = {ed.SystemProperties["iothub-connection-device-id"]}\r\n{JObject.Parse(Encoding.ASCII.GetString(ed.Body))}");
var msg = new {
EnqueuedTimeUtc = ed.SystemProperties["iothub-enqueuedtime"],
Properties = ed.Properties,
SystemProperties = new {
connectionDeviceId = ed.SystemProperties["iothub-connection-device-id"],
connectionAuthMethod = ed.SystemProperties["iothub-connection-auth-method"],
connectionDeviceGenerationId = ed.SystemProperties["iothub-connection-auth-generation-id"],
enqueuedTime = ed.SystemProperties["iothub-enqueuedtime"]
},
Body = JObject.Parse(Encoding.ASCII.GetString(ed.Body))
};
byte[] buffer = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(msg));
await outputBlob.UploadFromStreamAsync(new MemoryStream(buffer));
await Task.CompletedTask;
}
function.json:
{
"bindings": [
{
"name": "ed",
"connection": "rk2020iot_IOTHUB",
"eventHubName": "rk2020iot_IOTHUBNAME",
"consumerGroup": "function",
"cardinality": "one",
"direction": "in",
"type": "eventHubTrigger"
},
{
"name": "outputBlob",
"path": "iot/rk2020iot/{DateTime}.json",
"connection": "rk2020datalake2_STORAGE",
"direction": "out",
"type": "blob"
}
]
}

Related

Can't serialize GeoJson coordinates in .Net Core controller

I have a .net 6 API project that exports FeatureCollections. I'm using 'NetTopologySuite.IO.GeoJSON' version 2.0.4, and have a public API call like:
public async Task<IActionResult> ExportGeoJSON()
{
GeoJSON.Net.Feature.FeatureCollection ret = new GeoJSON.Net.Feature.FeatureCollection();
const double lat = -73.697913;
const double lon = 50.659193;
GeoJSON.Net.Geometry.Position coord = new GeoJSON.Net.Geometry.Position(lat, lon);
GeoJSON.Net.Geometry.Point pt = new GeoJSON.Net.Geometry.Point(coord);
GeoJSON.Net.Feature.Feature feat = new GeoJSON.Net.Feature.Feature(pt, null, Guid.NewGuid().ToString());
ret.Features.Add(feat);
return Ok(ret);
}
When i call this, i get back only:
{
"Type": "FeatureCollection",
"Features": [
{
"Type": "Feature",
"Id": "465f399d-b45c-47ed-b9e6-f395cd86b84b",
"Geometry": {
"Type": "Point"
}
},...
Ok, i look around and find out about GeoJSON4STJ https://github.com/NetTopologySuite/NetTopologySuite.IO.GeoJSON , so I put that into my Startup:
services.AddControllers()
.AddJsonOptions(opts =>
{
opts.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
opts.JsonSerializerOptions.PropertyNamingPolicy = null;
opts.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
opts.JsonSerializerOptions.Converters.Add(new NetTopologySuite.IO.Converters.GeoJsonConverterFactory());
});
and run it again, but no change at all. Exactly the same response. Am i missing something?
It seems that you are mixing libraries. NetTopologySuite.IO.GeoJSON (and NetTopologySuite.IO.GeoJSON4STJ) works with features from NetTopologySuite.Features package not from GeoJSON.Net.

IoT Hub Event Grid integration is creating two "Disconnected" events instead of a "Connected" and "Disconnected" event

I am using IoT Hub's Event Grid integration to monitor device connections and disconnections and am finding that I receive 2 "Disconnected" events and no "Connected" events. I am currently routing the Events to an Azure Function. Please let me know if I have configured it correctly.
IoT Hub Configuration:
Azure Function Code:
public static class IoTConnectionTrigger
{
//public static void Run([EventGridTrigger]EventGridEvent eventGridEvent, ILogger log)
[FunctionName("IoTConnectionTrigger")]
public static void Run([EventGridTrigger]JObject eventGridEvent, ILogger log)
{
log.LogInformation(eventGridEvent.ToString(Formatting.Indented));
}
}
}
Code used to create the IoT Hub message:
static void Main(string[] args)
{
for (int i=0; i<10; i++)
{
DeviceClient client = DeviceClient.CreateFromConnectionString("HostName=<IoT Hub Name>.azure-devices.net;DeviceId=SampleDevice;SharedAccessKey=<key>");
// Create JSON message
var telemetryDataPoint = new
{
Temperature = 50,
Location = "Calgary",
TimeStamp = DateTime.Now
};
var messageString = JsonConvert.SerializeObject(telemetryDataPoint);
Message message = new Message(Encoding.UTF8.GetBytes(messageString));
message.ContentType = "application/json";
message.ContentEncoding = "UTF-8";
try
{
client.SendEventAsync(message).Wait();
Console.WriteLine("Sent!");
} catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
Console.WriteLine("Complete");
}
Output: (Note the two "Microsoft.Devices.DeviceDisconnected" event types, and that no "Connected" events were raised)
Executing 'IoTConnectionTrigger' (Reason='EventGrid trigger fired at 2022-03-18T16:15:16.3717212+00:00', Id=84001f7e-6743-4847-ba73-48b597af33fa)
2022-03-18T16:15:16.372 [Information] {
"id": "de252911-9e70-b66f-954a-641d2c508040",
"topic": "/SUBSCRIPTIONS/7965FC25-694A-478C-B4FA-911F94239D30/RESOURCEGROUPS/TMRESOURCEGROUP/PROVIDERS/MICROSOFT.DEVICES/IOTHUBS/TMIOTHUB",
"subject": "devices/SampleDevice",
"eventType": "Microsoft.Devices.DeviceDisconnected",
"data": {
"deviceConnectionStateEventInfo": {
"sequenceNumber": "000000000000000001D80D890755FFF30000004500000000000000000000000B"
},
"hubName": "<IoT Hub Name>",
"deviceId": "SampleDevice"
},
"dataVersion": "",
"metadataVersion": "1",
"eventTime": "2022-03-18T16:14:36.8975903Z"
}
2022-03-18T16:15:16.372 [Information] Executed 'IoTConnectionTrigger' (Succeeded, Id=84001f7e-6743-4847-ba73-48b597af33fa, Duration=1ms)
2022-03-18T16:15:16.371 [Information] Executing 'IoTConnectionTrigger' (Reason='EventGrid trigger fired at 2022-03-18T16:15:16.3717212+00:00', Id=84001f7e-6743-4847-ba73-48b597af33fa)
2022-03-18T16:15:16.372 [Information] {
"id": "de252911-9e70-b66f-954a-641d2c508040",
"topic": "/SUBSCRIPTIONS/7965FC25-694A-478C-B4FA-911F94239D30/RESOURCEGROUPS/TMRESOURCEGROUP/PROVIDERS/MICROSOFT.DEVICES/IOTHUBS/TMIOTHUB",
"subject": "devices/SampleDevice",
"eventType": "Microsoft.Devices.DeviceDisconnected",
"data": {
"deviceConnectionStateEventInfo": {
"sequenceNumber": "000000000000000001D80D890755FFF30000004500000000000000000000000B"
},
"hubName": "<IoT Hub Name>",
"deviceId": "SampleDevice"
},
"dataVersion": "",
"metadataVersion": "1",
"eventTime": "2022-03-18T16:14:36.8975903Z"
Once a device is registered, the Hub start polling device status at a rate of about once per minute. So if a device is connected for 5 minutes, you should expect 5 reported connection events. If the device then disconnects for the next 3 minutes, you should expect 3 disconnection events. Because the sample is only taken once per minute, if a device manages to connect and then disconnect within a minute, the reported connection status may never actually report that it was connected. The way it works is documented here: https://learn.microsoft.com/en-us/azure/iot-hub/iot-hub-event-grid#limitations-for-device-connected-and-device-disconnected-events

Is it possible to expose the same Swagger JSON in both Swagger 2.0 and Open API 3 formats with Swashbuckle in ASP .NET Core?

We have a legacy application that only works with Swagger 2.0 JSON format. For everything else we would like to use Open API format.
Is there any way with Swashbuckle .NET Core to expose JSON in different formats under separate URLs? It looks like the SerializeAsV2 property in the UseSwagger method options is global for all endpoints.
Basically I would like to have the following end points that contain the same API data in different formats.
/swagger/v1/openapi/swagger.json
/swagger/v1/swagger2/swagger.json
An alternative approach is to split the request pipeline:
// serve v3 from /swagger/v1/swagger.json
app.UseSwagger(o => o.RouteTemplate = "swagger/{documentName}/swagger.json");
// serve v2 from /swagger2/v1/swagger.json
app.Map("/swagger2", swaggerApp =>
swaggerApp.UseSwagger(options => {
// note the dropped prefix "swagger/"
options.RouteTemplate = "{documentName}/swagger.json";
options.SerializeAsV2 = true;
})
);
You can serialize the OpenAPI document as V2 and serve it yourself. Taking SwaggerMiddleware as reference:
First register SwaggerGenerator:
services.AddTransient<SwaggerGenerator>();
Then inject SwaggerGenerator and build the document. Serve it from an endpoint or a controller. You can take the version as a path parameter to decide which version to serve.
app.UseEndpoints(e =>
{
// ...
e.MapGet("/swagger/{documentName}/swagger.{version}.json", context =>
{
var documentName = context.Request.RouteValues.GetValueOrDefault("documentName", null) as string;
var version = context.Request.RouteValues.GetValueOrDefault("version", null) as string;
if (documentName is null || version is null)
{
context.Response.StatusCode = StatusCodes.Status400BadRequest;
return Task.CompletedTask;
}
// inject SwaggerGenerator
var swaggerGenerator = context.RequestServices.GetRequiredService<SwaggerGenerator>();
var doc = swaggerGenerator.GetSwagger(documentName);
// serialize the document as json
using var writer = new StringWriter(CultureInfo.InvariantCulture);
var serializer = new OpenApiJsonWriter(writer);
if (version == "v2")
{
doc.SerializeAsV2(serializer);
}
else
{
doc.SerializeAsV3(serializer);
}
var json = writer.ToString();
// serve it as json
context.Response.ContentType = MediaTypeNames.Application.Json;
return context.Response.WriteAsync(json, new UTF8Encoding(false));
});
});
When you visit /swagger/v1/openapi.v2.json, you'll get the OpenAPI doc serialized as v2.
{
"swagger": "2.0",
"info": {
"title": "ApiPlayground",
"version": "1.0"
},
"paths": { ... }
}
Whereas /swagger/v1/openapi.v3.json will give you the doc serialized as v3:
{
"openapi": "3.0.1",
"info": {
"title": "ApiPlayground",
"version": "1.0"
},
"paths": { ... },
"components": { ... }
}

Consumer with message selector not working

I have a simple consumer:
try
{
factory = new NMSConnectionFactory(Settings.Endpoint);
connection = factory.CreateConnection(Settings.UserName, Settings.Password);
connection.ClientId = Settings.Name;
session = connection.CreateSession(AcknowledgementMode.AutoAcknowledge);
destination = SessionUtil.GetDestination(session, Settings.QueueName, DestinationType.Queue);
consumer = session.CreateConsumer(destination, "portCode = 'GB'", false);
consumer.Listener += new MessageListener(OnMessage);
}
catch
{
throw;
}
I need to apply a selector to get messages when the portCode field is equal to "GB".
This queue receives many messages.
The message is in JSON and a sample of this message is shown below:
{
"message": {
"list": [
{
xxxxxxx
}
]
},
"header": {
"messageCode": "xxxxxx",
"portCode": "GB",
"sourceSystem": "origin",
"messageId": "ca0bf0e0-cefa-4f5a-a80a-b518e7d2f645",
"dateTimeMessage": "2021-04-22T07:12:48.000-0300",
"version": "1.0"
}
}
However, I do not receive messages using the specified "GB" selector.
It seems simple to define selectors, but it is not working for me.
Thanks.
Selectors do not work on the body of the message (i.e. your JSON data). They only work on the headers and properties of the message.

How to extract a specific value from Json response and use this value in the input payload json file in Rest Assured Automation Testing

I am not using any POJO Classes. Instead i am using External json file as payload file for 2 of my API's (Get API and Delete API).
Add API >> Adds the book name, book shelf, other book details along with unique place_id.
Delete API >> Used to delete a book from specific rack using the unique place_id from above.
Since I am using external json payload input file, please let me know the way to pass the "place_id" grabbed from GET API and send this place_id to DELETE API external json file and then use it
Add Place API below: This API returns unique Place_ID in the response
{
"location": {
"lat": -38.383494,
"lng": 33.427362
},
"accuracy": 50,
"name": "Frontline house 1",
"phone_number": "(+91) 983 893 3937",
"address": "2951, side layout, cohen 09",
"types": [
"shoe park",
"shop"
],
"website": "http://google.com",
"language": "French-IN"
}
Delete Place API below:
{
"place_id": "<need to insert place_id from above response>"
}
You can do this using json-simple library in java. json-simple maven repository
imports
import io.restassured.RestAssured;
import io.restassured.path.json.JsonPath;
import io.restassured.response.Response;
import org.json.simple.JSONObject
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
You have to get to the following;
Retrieve place_id from Add Place API json response
Read request body of Delete Place API from json file
Replace the place_id value in request body of Delete Place API
Write the new request body to Delete Place API json file
// Retrieve place_id from Add Place API json response
Response response = RestAssured
.when()
.get("add_place_api_url")
.then()
.extract()
.response();
JsonPath jsonPath = response.jsonPath();
String placeId = jsonPath.get("place_id");
// Read request body of Delete Place API from json file
JSONObject jsonObject = new JSONObject();
JSONParser parser = new JSONParser();
try {
Object obj = parser.parse(new FileReader("path/DeletePlaceRequestBody.json"));
jsonObject = (JSONObject) obj;
// Replace the place_id value in request body of Delete Place API
jsonObject.replace("place_id", placeId);
} catch (IOException e) {
e.printStackTrace();
} catch (ParseException e) {
e.printStackTrace();
}
// Write the new request body to Delete Place API json file
try {
FileWriter file = new FileWriter("DeletePlaceRequestBody.json");
file.write(jsonObject.toString());
file.flush();
file.close();
} catch (IOException e) {
e.printStackTrace();
}