I have a response from API like this:
{
"data": {
"items": [
{
"jsonBody": "{\n \"documentInfo\": {\n \"docId\": \"AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA\",\n \"docCreateTimestamp\": 56565687867,\n \"docUpdateTimestamp\": 56465755766,\n \"docSynchTimestamp\": 56565687867,\n \"documentType\": \"document-monthly-instagram\",\n \"documentName\": \"Monthly Instagram Posts\",\n \"docDescription\": \"Monthly Instagram Posts\"\n },\n \"documentData\": {\n \"header\": {\n \"configuration\": {\n \"autoGenerate\": [\n {\n \"id\": \"headerDocNumberId\",\n \"type\": \"8code\"\n },\n {\n \"id\": \"headerVersionNumberId\",\n \"type\": \"autonum\"\n },\n {\n \"id\": \"headerDocumentCreationDateId\",\n \"type\": \"date\"\n }\n ],\n \"autoFill\": [],\n \"tileTitle\": \"headerDocumentTileNameId\",\n \"tileView\": [\n \"headerDocumentTileNameId\",\n \"headerDetailsId\",\n \"headerDocumentCreationDateId\"\n ],\n \"createView\": [\n \"headerMonthNameId\",\n \"headerNameId\",\n \"headerDetailsId\"\n ],\n \"editView\": [\n \"headerMonthNameId\",\n \"headerNameId\",\n \"headerDetailsId\"\n ]\n },\n \"data\": [\n {\n \"id\": \"headerDocNumberId\",\n \"name\": \"Document Number\",\n \"attr\": \"\",\n \"placeholder\": \"Enter An Unique Value\",\n \"value\": \"\",\n \"type\": \"text\",\n \"extType\": \"\",\n \"tag\": \"\",\n \"readonly\": false,\n \"required\": true,\n \"keyboardType\": \"\"\n },\n {\n \"id\": \"headerVersionNumberId\",\n \"name\": \"Document Version\",\n \"attr\": \"\",\n \"placeholder\": \"Enter A Number\",\n \"value\": 1,\n \"type\": \"int\",\n \"extType\": \"\",\n \"tag\": \"\",\n \"readonly\": true,\n \"required\": true,\n \"keyboardType\": \"\"\n },\n {\n \"id\": \"headerDocumentCreationDateId\",\n \"name\": \"Time & Date\",\n \"attr\": \"\",\n \"placeholder\": \"Enter Date\",\n \"value\": \"\",\n \"type\": \"text\",\n \"extType\": \"\",\n \"tag\": \"\",\n \"readonly\": true,\n \"required\": true,\n \"keyboardType\": \"\"\n },\n {\n \"id\": \"headerNameId\",\n \"name\": \"Name\",\n \"attr\": \"\",\n \"placeholder\": \"Enter Name\",\n \"value\": \"\",\n \"type\": \"text\",\n \"extType\": \"\",\n \"tag\": \"\",\n \"readonly\": true,\n \"required\": true,\n \"keyboardType\": \"\"\n },\n {\n \"id\": \"headerDocumentTileNameId\",\n \"name\": \"Document Type\",\n \"attr\": \"\",\n \"placeholder\": \"\",\n \"value\": \"Monthly Log Book\",\n \"type\": \"text\",\n \"extType\": \"\",\n \"readonly\": true,\n \"requred\": true,\n \"keyboardType\": \"\"\n },\n {\n \"id\": \"headerMonthNameId\",\n \"name\": \"Month\",\n \"attr\": \"\",\n \"placeholder\": \"Enter Post Month\",\n \"value\": \"\",\n \"type\": \"list\",\n \"extType\": \"monthTypeListId\",\n \"readonly\": false,\n \"requred\": false,\n \"keyboardType\": \"\"\n },\n {\n \"id\": \"headerDetailsId\",\n \"name\": \"Details\",\n \"attr\": \"\",\n \"placeholder\": \"Enter Details\",\n \"value\": \"\",\n \"type\": \"text\",\n \"extType\": \"\",\n \"readonly\": false,\n \"requred\": false,\n \"keyboardType\": \"\"\n }\n ]\n },\n \"content\": {\n \"groups\": [\n {\n \"name\": \"Week 1\",\n \"sectionType\": {\n \"extendable\": \"dynamic\",\n \"allowedTypeIds\": [\n \"typePostId\"\n ]\n },\n \"sections\": [\n ]\n },\n {\n \"name\": \"Week 2\",\n \"sectionType\": {\n \"extendable\": \"dynamic\",\n \"allowedTypeIds\": [\n \"typePostId\"\n ]\n },\n \"sections\": [\n ]\n },\n {\n \"name\": \"Week 3\",\n \"sectionType\": {\n \"extendable\": \"dynamic\",\n \"allowedTypeIds\": [\n \"typePostId\"\n ]\n },\n \"sections\": [\n ]\n },\n {\n \"name\": \"Week 4\",\n \"sectionType\": {\n \"extendable\": \"dynamic\",\n \"allowedTypeIds\": [\n \"typePostId\"\n ]\n },\n \"sections\": [\n ]\n }\n ]\n },\n \"summary\": \"\"\n },\n \"documentStructure\": {\n \"sectionTypes\": [\n {\n \"id\": \"typePostId\",\n \"name\": \"INSTAGRAM POST\",\n \"order\": 0,\n \"rowType\": {\n \"extendable\": \"static\",\n \"allowedTypeIds\": [\n \"rowDateId\",\n \"rowNameOfEventId\",\n \"rowDescriptionId\",\n \"rowHashTagsId\",\n \"rowLocationId\",\n \"rowPeopleLinksId\",\n \"rowLogosId\",\n \"rowPicturesVideoId\",\n \"rowPromoteId\",\n \"rowDestinationId\",\n \"rowAudienceId\",\n \"rowBudgetId\"\n ]\n },\n \"rows\": [\n ],\n \"collapsed\": false\n }\n ],\n \"rowTypes\": [\n {\n \"id\": \"rowDateId\",\n \"name\": \"Date\",\n \"attr\": \"Select date\",\n \"placeholder\": \"Enter Trip Date\",\n \"value\": \"\",\n \"type\": \"date\",\n \"extType\": \"\",\n \"readonly\": false,\n \"requred\": false,\n \"keyboardType\": \"\",\n \"col\": 2\n },\n {\n \"id\": \"rowNameOfEventId\",\n \"name\": \"Name of Event\",\n \"attr\": \"Enter name of event\",\n \"placeholder\": \"Enter name of event\",\n \"value\": \"\",\n \"type\": \"textArea\",\n \"extType\": \"512\",\n \"readonly\": false,\n \"requred\": false,\n \"keyboardType\": \"\",\n \"col\": 3\n },\n {\n \"id\": \"rowDescriptionId\",\n \"name\": \"Decription of Event\",\n \"attr\": \"Enter description of event\",\n \"placeholder\": \"Enter description\",\n \"value\": \"\",\n \"type\": \"textArea\",\n \"extType\": \"512\",\n \"readonly\": false,\n \"requred\": false,\n \"keyboardType\": \"\",\n \"col\": 4\n },\n {\n \"id\": \"rowHashTagsId\",\n \"name\": \"Hash Tags\",\n \"attr\": \"Enter hash tags\",\n \"placeholder\": \"Enter hash tags\",\n \"value\": \"\",\n \"type\": \"textArea\",\n \"extType\": \"512\",\n \"readonly\": false,\n \"requred\": false,\n \"keyboardType\": \"\",\n \"col\": 5\n },\n {\n \"id\": \"rowLocationId\",\n \"name\": \"Location\",\n \"attr\": \"Select location\",\n \"placeholder\": \"Enter location\",\n \"value\": {\n \"description\": \"\",\n \"location\": {}\n },\n \"type\": \"address\",\n \"extType\": \"300\",\n \"readonly\": false,\n \"requred\": false,\n \"keyboardType\": \"\",\n \"col\": 6\n },\n {\n \"id\": \"rowPeopleLinksId\",\n \"name\": \"People links\",\n \"attr\": \"Enter people links\",\n \"placeholder\": \"Enter people links\",\n \"value\": \"\",\n \"type\": \"textArea\",\n \"extType\": \"512\",\n \"readonly\": false,\n \"requred\": false,\n \"keyboardType\": \"\",\n \"col\": 7\n },\n {\n \"id\": \"rowLogosId\",\n \"name\": \"Logos\",\n \"attr\": \"Logos\",\n \"placeholder\": \"\",\n \"value\": [],\n \"type\": \"photo\",\n \"extType\": \"3\",\n \"readonly\": false,\n \"requred\": false,\n \"keyboardType\": \"\",\n \"col\": 8\n },\n {\n \"id\": \"rowPicturesVideoId\",\n \"name\": \"Pictures/Video Records\",\n \"attr\": \"Pictures/Video\",\n \"placeholder\": \"\",\n \"value\": [],\n \"type\": \"video\",\n \"extType\": \"5\",\n \"readonly\": false,\n \"requred\": false,\n \"keyboardType\": \"\",\n \"col\": 9\n },\n {\n \"id\": \"rowPromoteId\",\n \"name\": \"Promote\",\n \"attr\": \"\",\n \"placeholder\": \"\",\n \"value\": false,\n \"type\": \"bool\",\n \"extType\": \"\",\n \"readonly\": false,\n \"requred\": false,\n \"keyboardType\": \"\",\n \"col\": 10\n },\n {\n \"id\": \"rowDestinationId\",\n \"name\": \"Destination\",\n \"attr\": \"Please select destination\",\n \"placeholder\": \"Select destination\",\n \"value\": \"iDestinationProfileId\",\n \"type\": \"list\",\n \"extType\": \"DestinationTypeListId\",\n \"readonly\": false,\n \"requred\": false,\n \"keyboardType\": \"\",\n \"col\": 11\n },\n {\n \"id\": \"rowAudienceId\",\n \"name\": \"Audience\",\n \"attr\": \"Please select audience to promote\",\n \"placeholder\": \"Select audience\",\n \"value\": \"iAudienceAutomaticId\",\n \"type\": \"list\",\n \"extType\": \"AudienceTypeListId\",\n \"readonly\": false,\n \"requred\": false,\n \"keyboardType\": \"\",\n \"col\": 12\n },\n {\n \"id\": \"rowBudgetId\",\n \"name\": \"Budget&Duration\",\n \"attr\": \"Please choose Budget or Duration\",\n \"placeholder\": \"Select Budget or Duration\",\n \"value\": \"iBudgetBudgetId\",\n \"type\": \"list\",\n \"extType\": \"BudgetTypeListId\",\n \"readonly\": false,\n \"requred\": false,\n \"keyboardType\": \"\",\n \"col\": 13\n }\n ],\n \"listTypes\": [\n {\n \"id\": \"DestinationTypeListId\",\n \"items\": [\n {\n \"id\": \"iDestinationProfileId\",\n \"value\": \"Your Profile\",\n \"selected\": true\n },\n {\n \"id\": \"iDestinationWebsiteId\",\n \"value\": \"Your Website\",\n \"selected\": false\n },\n {\n \"id\": \"iDestinationDirectId\",\n \"value\": \"Your Direct Messages\",\n \"selected\": false\n }\n ]\n },\n {\n \"id\": \"AudienceTypeListId\",\n \"items\": [\n {\n \"id\": \"iAudienceAutomaticId\",\n \"value\": \"Automatic\",\n \"selected\": true\n },\n {\n \"id\": \"iAudienceKUId\",\n \"value\": \"Kite Union Clients\",\n \"selected\": false\n },\n {\n \"id\": \"iAudienceCreateId\",\n \"value\": \"Create Your Own\",\n \"selected\": false\n }\n ]\n },\n {\n \"id\": \"BudgetTypeListId\",\n \"items\": [\n {\n \"id\": \"iBudgetBudgetId\",\n \"value\": \"Budget\",\n \"selected\": true\n },\n {\n \"id\": \"iBudgetDurationId\",\n \"value\": \"Duration\",\n \"selected\": false\n }\n ]\n },\n {\n \"id\": \"monthTypeListId\",\n \"items\": [\n {\n \"id\": \"iMonthTypeJanuaryId\",\n \"value\": \"January\",\n \"selected\": false\n },\n {\n \"id\": \"iMonthTypeFebruaryId\",\n \"value\": \"February\",\n \"selected\": false\n },\n {\n \"id\": \"iMonthTypeMarchId\",\n \"value\": \"March\",\n \"selected\": false\n },\n {\n \"id\": \"iMonthTypeAprilId\",\n \"value\": \"April\",\n \"selected\": false\n },\n {\n \"id\": \"iMonthTypeMayId\",\n \"value\": \"May\",\n \"selected\": false\n },\n {\n \"id\": \"iMonthTypeJuneId\",\n \"value\": \"June\",\n \"selected\": false\n },\n {\n \"id\": \"iMonthTypeJulyId\",\n \"value\": \"July\",\n \"selected\": false\n },\n {\n \"id\": \"iMonthTypeAugustId\",\n \"value\": \"August\",\n \"selected\": false\n },\n {\n \"id\": \"iMonthTypeSeptemberId\",\n \"value\": \"September\",\n \"selected\": false\n },\n {\n \"id\": \"iMonthTypeOctoberId\",\n \"value\": \"October\",\n \"selected\": false\n },\n {\n \"id\": \"iMonthTypeNovemberId\",\n \"value\": \"November\",\n \"selected\": false\n },\n {\n \"id\": \"iMonthTypeDecemberId\",\n \"value\": \"December\",\n \"selected\": false\n }\n ]\n }\n ]\n }\n}"
}
]
},
"code": 0,
"message": ""
}
I want to convert the value of "jsonBody" which is string into Codable Model.
I have tried to convert it into [String: Array] but then when I am assigning my model to the result it gives me this error:
typeMismatch(Swift.Dictionary<Swift.String, Any>, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "jsonBody", intValue: nil)], debugDescription: "Expected to decode Dictionary<String, Any> but found a string/data instead.", underlyingError: nil))
Nothing really complicated about that, the item has to use a secondary JsonDecoder:
struct Item: Decodable {
let body: Body
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let jsonBody = try container.decode(String.self, forKey: .jsonBody)
let jsonBodyData = jsonBody.data(using: .utf8)!
let decoder = JSONDecoder()
body = try decoder.decode(Body.self, from: jsonBodyData)
}
private enum CodingKeys: String, CodingKey {
case jsonBody
}
}
struct Body: Decodable {
let documentInfo: ...
}
This was a tough one. Hope this helps someone else.
I am parsing data from a JSON file. Here is the code to get the parse the JSON:
guard let url = Bundle.main.url(forResource: "SYNC_DATA", withExtension: "json") else {
fatalError("Failed to locate SYNC_DATA in bundle")
}
if let data = try? Data(contentsOf: url) {
let decoder = JSONDecoder()
if let syncData = try? decoder.decode(SyncResponse.self, from: data) {
print("Successfully decoded sync data!!!")
print("Sync Data = \(syncData)")
}
} else {
fatalError("Failed to load SYNC_DATA from bundle")
}
Here is the Codable:
struct SyncResponse: Codable {
//MARK: items currently in the sync download
var serverTimestamp: String
var status: Bool
//MARK: init method used to decode the data for this object
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.serverTimestamp = try container.decode(String.self, forKey: .serverTimestamp)
self.status = try container.decode(Bool.self, forKey: .status)
....(other data to decode)
}
enum CodingKeys: String, CodingKey {
case serverTimestamp = "serverTimestamp"
case status = "status"
....(other data)
}
}
I am following the instructions explained in this documentation to connect Apache Kafka to Eclipse Ditto.
https://www.eclipse.org/ditto/connectivity-protocol-bindings-kafka2.html
I am not sure about the following.
1) ["ditto:outbound-auth-subject", "..."] under the Authorization context.
2) "address": "topic/key"
Please let me know about them! Thank you in advance.!
Edit:
Please find the command that I used to connect Ditto and Kafka
curl -X POST -i -u devops:foobar -H 'Content-Type: application/json' -d '{
"targetActorSelection": "/system/sharding/connection",
"headers": {
"aggregate": false
},
"piggybackCommand": {
"type": "connectivity.commands:createConnection",
"connection": {
"id": "MyKafkaConnection1",
"connectionType": "kafka",
"connectionStatus": "open",
"uri": "tcp://radsah:password#localhost:9092",
"specificConfig": {
"bootstrapServers": "10.196.2.218:9092",
"saslMechanism": "plain"
},
"failoverEnabled": true,
"targets": [
{
"address": "digital-twins",
"topics": [
"_/_/things/twin/events",
"_/_/things/live/messages"
],
"authorizationContext": ["ditto:outbound-auth-subject"]
}],
"mappingContext": {
"mappingEngine": "JavaScript",
"options": {
"incomingScript": "function mapToDittoProtocolMsg(\n headers,\n textPayload,\n bytePayload,\n contentType\n) {\n\n if (contentType !== \"application/json\") {\n return null;\n }\n\n var jsonData = JSON.parse(textPayload);\n var temperature = jsonData.temp;\n var humidity = jsonData.hum;\n \n var path;\n var value;\n if (temperature != null && humidity != null) {\n path = \"/features\";\n value = {\n temperature: {\n properties: {\n value: temperature\n }\n },\n humidity: {\n properties: {\n value: humidity\n }\n }\n };\n } else if (temperature != null) {\n path = \"/features/temperature/properties/value\";\n value = temperature;\n } else if (humidity != null) {\n path = \"/features/humidity/properties/value\";\n value = humidity;\n }\n \n if (!path || !value) {\n return null;\n }\n\n return Ditto.buildDittoProtocolMsg(\n \"org.eclipse.ditto\",\n headers[\"device_id\"],\n \"things\",\n \"twin\",\n \"commands\",\n \"modify\",\n path,\n headers,\n value\n );\n}"
}
}
}
}
}' http://localhost:8080/devops/piggyback/connectivity?timeout=8000
I have registered a device using Hono and I am sending the data to Ditto. Ditto successfully receives the data. But I want send this received data to Kafka.
Connection is successfully established between Kafka and Ditto. But I am not receiving at the kafka-consumer "digital-twins". Am I missing something?
Edited with the Policy command:
curl -X PUT 'http://localhost:8080/api/2/policies/org.eclipse.ditto:5100' -u 'ditto:ditto' -H 'Content-Type: application/json' -d '{
"entries": {
"owner": {
"subjects": {
"nginx:ditto": {
"type": "nginx basic auth user"
}
},
"resources": {
"thing:/": {
"grant": [
"READ","WRITE"
],
"revoke": []
},
"policy:/": {
"grant": [
"READ","WRITE"
],
"revoke": []
},
"message:/": {
"grant": [
"READ","WRITE"
],
"revoke": []
}
}
}
}
}
regarding the authorization context you can have a look at the authorization section in our connections documentation. It has to hold a subject that is defined in the policy or ACL of your things.
As example:
The policy of Thing "foo:bar" has read access of the whole thing for subject "somePrefix:someValue" defined.
{
"policyId": "foo:bar",
"entries": {
... //Maybe more entries
"MyKafkaConnection": {
"subjects": {
"somePrefix:someValue": {
"type": "my description for this subject"
}
},
"resources": {
"thing:/": {
"grant": [
"READ"
],
"revoke": []
},
"message:/": {
"grant": [
"READ"
],
"revoke": []
}
}
}
}
}
In the sample you referring to, the events related to "foo:bar" would then be published via the kafka connection on the topic you specified in the address field.