Aglio builder does not put object to schema if this object was defined as an array option - apiblueprint

Here I'm defining a property in object Page Index called chartControllers which must be an array of objects called chartController.
# Page Index (Page Base)
- bundle: `site-index` (string, required) - название страницы на фронтенде
- nav (object, required) - навигационное меню
- settings: `/settings` (string, required) - юрл по которому будет осуществляться переход на страницу настроек
- signOut: `/signOut` (string, required) - юрл по которому будет отправляться POST запрос для выхода из аккаунта
- chart (object, required) - данные для первоначальной отрисовки графика, до выбора каких либо опций
- title (object, required)
- text: `выберите модуль` (string, required) - надпись в заголовке графика
- chartControllers (array[chartController], required) - массив всевозможных опций меню
# chartController
- title: `выбор инстанса` (string, required) - названии типа настройки в меню
- action: `/page` (string, required) - url по которому совершает данная настройка запрос при выборе ее опции(опций)
- name: `page` (string, required) - уникальное человекочитаемое имя настройки на английском языке
- method: `POST` (string, optional) - метод совершения запроса
aglio builder defines object Page Index correctly, with only one peculiar moment - it does not describe options for object `chartController`.
It provides me with the following body, which is fine:
{
"title": "главная",
"bundle": "site-index",
"nav": {
"settings": "/settings",
"signOut": "/signOut"
},
"chart": {
"title": {
"text": "выберите модуль"
}
},
"chartControllers": [
{
"title": "выбор инстанса",
"action": "/page",
"name": "page",
"method": "POST"
}
]
}
BUT! It provides me with not complete schema! It does not describe chartController.
The schema is the following:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"title": {
"type": "string",
"description": "Название страницы в табе браузера."
},
"bundle": {
"type": "string",
"description": "название страницы на фронтенде"
},
"nav": {
"type": "object",
"properties": {
"settings": {
"type": "string",
"description": "юрл по которому будет осуществляться переход на страницу настроек"
},
"signOut": {
"type": "string",
"description": "юрл по которому будет отправляться POST запрос для выхода из аккаунта"
}
},
"required": [
"settings",
"signOut"
],
"description": "навигационное меню"
},
"chart": {
"type": "object",
"properties": {
"title": {
"type": "object",
"properties": {
"text": {
"type": "string",
"description": "надпись в заголовке графика"
}
},
"required": [
"text"
]
}
},
"required": [
"title"
],
"description": "данные для первоначальной отрисовки графика, до выбора каких либо опций"
},
"chartControllers": {
"type": "array",
"description": "массив всевозможных опций меню"
}
},
"required": [
"title",
"bundle",
"nav",
"chart",
"chartControllers"
]
}
How can I fix that and see chartController to be defined in schema?

I had the same question as you, after searching the web for a long time, finally I found the solution:
Attributes with array of objects produce incomplete schema #328
Here is the fix for your particular question as an example:
(just add 'fixed', although I have know idea where this 'fixed' came from.)
# Page Index (Page Base)
......
- chartControllers (array[chartController], required, fixed) - массив всевозможных опций меню

Related

get youtube videos listed in subsection of channel's 'videos'

How can I get videos listed in an item of the dropdown menu of a channel's 'videos' tab?
Is it a playlist with an ID? If I wanted to get all the uploaded videos, I could first query the id of playlist 'uploads' (here for channel UCj7IJ427cnavyq2ZOpSKYfA):
GET https://youtube.googleapis.com/youtube/v3/channels?part=contentDetails&id=UCj7IJ427cnavyq2ZOpSKYfA&key=[YOUR_API_KEY] HTTP/1.1
{
"kind": "youtube#channelListResponse",
...,
"items": [
{
"kind": "youtube#channel",
...,
"contentDetails": {
"relatedPlaylists": {
"likes": "",
"favorites": "",
"uploads": "UUj7IJ427cnavyq2ZOpSKYfA" # <- here it is!
}
}
}
]
}
and use it to run playlistItems. But what about that 'Upcoming livestreams' part only?
You should use the Search.list API endpoint, invoked with the request parameters channelId, type and eventType set accordingly:
GET https://www.googleapis.com/youtube/v3/search?key=${API_KEY}&type=video&eventType=upcoming&part=snippet&maxResults=50&channelId=${CHANNEL_ID},
where ${CHANNEL_ID} is the ID the the channel of your interest and ${API_KEY} is your API key.
For the channel you mentioned above -- that with ID UCj7IJ427cnavyq2ZOpSKYfA --, the JSON response looked like:
{
"kind": "youtube#searchListResponse",
"etag": "...",
"regionCode": "...",
"pageInfo": {
"totalResults": 4,
"resultsPerPage": 4
},
"items": [
{
"kind": "youtube#searchResult",
"etag": "quPJtDE4C_PAgFJU9QIjA5d_C4k",
"id": {
"kind": "youtube#video",
"videoId": "JksecRTzbgo"
},
"snippet": {
"publishedAt": "2021-05-19T10:33:39Z",
"channelId": "UCj7IJ427cnavyq2ZOpSKYfA",
"title": "LIVE: Т. Владимиров, фортепиано. Бах, Рахманинов, Шуберт",
"description": "Тимофей ВЛАДИМИРОВ, фортепиано В программе: И.С. Бах. Шестиголосный ричеркар из цикла «Музыкальное приношение» Рамо – Годовский.",
"thumbnails": {
...
},
"channelTitle": "MOSCOW INTERNATIONAL PERFORMING ARTS CENTER",
"liveBroadcastContent": "upcoming",
"publishTime": "2021-05-19T10:33:39Z"
}
},
{
"kind": "youtube#searchResult",
"etag": "7LTWdzCS0NDFayU3zbfDcb3xky4",
"id": {
"kind": "youtube#video",
"videoId": "G85Prxi4Mr0"
},
"snippet": {
"publishedAt": "2021-05-25T12:28:10Z",
"channelId": "UCj7IJ427cnavyq2ZOpSKYfA",
"title": "LIVE: Лауреаты IV Конкурса пианистов В. Крайнева в возрастной категории от 16 до 22 лет",
"description": "В День защиты детей Концерт победителей и лауреатов IV Международного конкурса пианистов Владимира Крайнева в возрастной категории от 16 ...",
"thumbnails": {
...
},
"channelTitle": "MOSCOW INTERNATIONAL PERFORMING ARTS CENTER",
"liveBroadcastContent": "upcoming",
"publishTime": "2021-05-25T12:28:10Z"
}
},
{
"kind": "youtube#searchResult",
"etag": "I4FOz2ykBQviPr_7vdF87240iG8",
"id": {
"kind": "youtube#video",
"videoId": "tKDEjh37BrA"
},
"snippet": {
"publishedAt": "2021-05-25T12:01:48Z",
"channelId": "UCj7IJ427cnavyq2ZOpSKYfA",
"title": "LIVE: Лауреаты IV Конкурса пианистов В. Крайнева в возрастной категории от 7 до 12 лет",
"description": "В День защиты детей Концерт победителей и лауреатов IV Международного конкурса пианистов Владимира Крайнева в возрастной категории от 7 до ...",
"thumbnails": {
...
},
"channelTitle": "MOSCOW INTERNATIONAL PERFORMING ARTS CENTER",
"liveBroadcastContent": "upcoming",
"publishTime": "2021-05-25T12:01:48Z"
}
},
{
"kind": "youtube#searchResult",
"etag": "tpF_wpRF43W6fv1MGpNnCBSvCDU",
"id": {
"kind": "youtube#video",
"videoId": "R9Em6BJW0e0"
},
"snippet": {
"publishedAt": "2021-05-25T12:08:55Z",
"channelId": "UCj7IJ427cnavyq2ZOpSKYfA",
"title": "LIVE: Лауреаты IV Конкурса пианистов В. Крайнева в возрастной категории от 13 до 15 лет",
"description": "В День защиты детей Концерт победителей и лауреатов IV Международного конкурса пианистов Владимира Крайнева в возрастной категории от 13 ...",
"thumbnails": {
...
},
"channelTitle": "MOSCOW INTERNATIONAL PERFORMING ARTS CENTER",
"liveBroadcastContent": "upcoming",
"publishTime": "2021-05-25T12:08:55Z"
}
}
]
}
Do note that the Search.list endpoint is relatively expensive: each call of it will cost you 100 units of quota (accounted from your daily quota, which by default is 10000 units).

elasticsearch filebeat mapper_parsing_exception when using decode_json_fields

I have ECK setup and im using filebeat to ship logs from Kubernetes to elasticsearch.
Ive recently added decode_json_fields processor to my configuration, so that im able decode the json that is usually in the message field.
- decode_json_fields:
fields: ["message"]
process_array: false
max_depth: 10
target: "log"
overwrite_keys: true
add_error_key: true
However logs have stopped appearing since adding it.
example log:
{
"_index": "filebeat-7.9.1-2020.10.01-000001",
"_type": "_doc",
"_id": "wF9hB3UBtUOF3QRTBcts",
"_score": 1,
"_source": {
"#timestamp": "2020-10-08T08:43:18.672Z",
"kubernetes": {
"labels": {
"controller-uid": "9f3f9d08-cfd8-454d-954d-24464172fa37",
"job-name": "stream-hatchet-cron-manual-rvd"
},
"container": {
"name": "stream-hatchet-cron",
"image": "<redacted>.dkr.ecr.us-east-2.amazonaws.com/stream-hatchet:v0.1.4"
},
"node": {
"name": "ip-172-20-32-60.us-east-2.compute.internal"
},
"pod": {
"uid": "041cb6d5-5da1-4efa-b8e9-d4120409af4b",
"name": "stream-hatchet-cron-manual-rvd-bh96h"
},
"namespace": "default"
},
"ecs": {
"version": "1.5.0"
},
"host": {
"mac": [],
"hostname": "ip-172-20-32-60",
"architecture": "x86_64",
"name": "ip-172-20-32-60",
"os": {
"codename": "Core",
"platform": "centos",
"version": "7 (Core)",
"family": "redhat",
"name": "CentOS Linux",
"kernel": "4.9.0-11-amd64"
},
"containerized": false,
"ip": []
},
"cloud": {
"instance": {
"id": "i-06c9d23210956ca5c"
},
"machine": {
"type": "m5.large"
},
"region": "us-east-2",
"availability_zone": "us-east-2a",
"account": {
"id": "<redacted>"
},
"image": {
"id": "ami-09d3627b4a09f6c4c"
},
"provider": "aws"
},
"stream": "stdout",
"message": "{\"message\":{\"log_type\":\"cron\",\"status\":\"start\"},\"level\":\"info\",\"timestamp\":\"2020-10-08T08:43:18.670Z\"}",
"input": {
"type": "container"
},
"log": {
"offset": 348,
"file": {
"path": "/var/log/containers/stream-hatchet-cron-manual-rvd-bh96h_default_stream-hatchet-cron-73069980b418e2aa5e5dcfaf1a29839a6d57e697c5072fea4d6e279da0c4e6ba.log"
}
},
"agent": {
"type": "filebeat",
"version": "7.9.1",
"hostname": "ip-172-20-32-60",
"ephemeral_id": "6b3ba0bd-af7f-4946-b9c5-74f0f3e526b1",
"id": "0f7fff14-6b51-45fc-8f41-34bd04dc0bce",
"name": "ip-172-20-32-60"
}
},
"fields": {
"#timestamp": [
"2020-10-08T08:43:18.672Z"
],
"suricata.eve.timestamp": [
"2020-10-08T08:43:18.672Z"
]
}
}
In the filebeat logs i can see the following error:
2020-10-08T09:25:43.562Z WARN [elasticsearch] elasticsearch/client.go:407 Cannot
index event
publisher.Event{Content:beat.Event{Timestamp:time.Time{wall:0x36b243a0,
ext:63737745936, loc:(*time.Location)(nil)}, Meta:null,
Fields:{"agent":{"ephemeral_id":"5f8afdba-39c3-4fb7-9502-be7ef8f2d982","hostname":"ip-172-20-32-60","id":"0f7fff14-6b51-45fc-8f41-34bd04dc0bce","name":"ip-172-20-32-60","type":"filebeat","version":"7.9.1"},"cloud":{"account":{"id":"700849607999"},"availability_zone":"us-east-2a","image":{"id":"ami-09d3627b4a09f6c4c"},"instance":{"id":"i-06c9d23210956ca5c"},"machine":{"type":"m5.large"},"provider":"aws","region":"us-east-2"},"ecs":{"version":"1.5.0"},"host":{"architecture":"x86_64","containerized":false,"hostname":"ip-172-20-32-60","ip":["172.20.32.60","fe80::af:9fff:febe:dc4","172.17.0.1","100.96.1.1","fe80::6010:94ff:fe17:fbae","fe80::d869:14ff:feb0:81b3","fe80::e4f3:b9ff:fed8:e266","fe80::1c19:bcff:feb3:ce95","fe80::fc68:21ff:fe08:7f24","fe80::1cc2:daff:fe84:2a5a","fe80::3426:78ff:fe22:269a","fe80::b871:52ff:fe15:10ab","fe80::54ff:cbff:fec0:f0f","fe80::cca6:42ff:fe82:53fd","fe80::bc85:e2ff:fe5f:a60d","fe80::e05e:b2ff:fe4d:a9a0","fe80::43a:dcff:fe6a:2307","fe80::581b:20ff:fe5f:b060","fe80::4056:29ff:fe07:edf5","fe80::c8a0:5aff:febd:a1a3","fe80::74e3:feff:fe45:d9d4","fe80::9c91:5cff:fee2:c0b9"],"mac":["02:af:9f:be:0d:c4","02:42:1b:56:ee:d3","62:10:94:17:fb:ae","da:69:14:b0:81:b3","e6:f3:b9:d8:e2:66","1e:19:bc:b3:ce:95","fe:68:21:08:7f:24","1e:c2:da:84:2a:5a","36:26:78:22:26:9a","ba:71:52:15:10:ab","56:ff:cb:c0:0f:0f","ce:a6:42:82:53:fd","be:85:e2:5f:a6:0d","e2:5e:b2:4d:a9:a0","06:3a:dc:6a:23:07","5a:1b:20:5f:b0:60","42:56:29:07:ed:f5","ca:a0:5a:bd:a1:a3","76:e3:fe:45:d9:d4","9e:91:5c:e2:c0:b9"],"name":"ip-172-20-32-60","os":{"codename":"Core","family":"redhat","kernel":"4.9.0-11-amd64","name":"CentOS
Linux","platform":"centos","version":"7
(Core)"}},"input":{"type":"container"},"kubernetes":{"container":{"image":"700849607999.dkr.ecr.us-east-2.amazonaws.com/stream-hatchet:v0.1.4","name":"stream-hatchet-cron"},"labels":{"controller-uid":"a79daeac-b159-4ba7-8cb0-48afbfc0711a","job-name":"stream-hatchet-cron-manual-c5r"},"namespace":"default","node":{"name":"ip-172-20-32-60.us-east-2.compute.internal"},"pod":{"name":"stream-hatchet-cron-manual-c5r-7cx5d","uid":"3251cc33-48a9-42b1-9359-9f6e345f75b6"}},"log":{"level":"info","message":{"log_type":"cron","status":"start"},"timestamp":"2020-10-08T09:25:36.916Z"},"message":"{"message":{"log_type":"cron","status":"start"},"level":"info","timestamp":"2020-10-08T09:25:36.916Z"}","stream":"stdout"},
Private:file.State{Id:"native::30998361-66306", PrevId:"",
Finished:false, Fileinfo:(*os.fileStat)(0xc001c14dd0),
Source:"/var/log/containers/stream-hatchet-cron-manual-c5r-7cx5d_default_stream-hatchet-cron-4278d956fff8641048efeaec23b383b41f2662773602c3a7daffe7c30f62fe5a.log",
Offset:539, Timestamp:time.Time{wall:0xbfd7d4a1e556bd72,
ext:916563812286, loc:(*time.Location)(0x607c540)}, TTL:-1,
Type:"container", Meta:map[string]string(nil),
FileStateOS:file.StateOS{Inode:0x1d8ff59, Device:0x10302},
IdentifierName:"native"}, TimeSeries:false}, Flags:0x1,
Cache:publisher.EventCache{m:common.MapStr(nil)}} (status=400):
{"type":"mapper_parsing_exception","reason":"failed to parse field
[log.message] of type [keyword] in document with id
'56aHB3UBLgYb8gz801DI'. Preview of field's value: '{log_type=cron,
status=start}'","caused_by":{"type":"illegal_state_exception","reason":"Can't
get text on a START_OBJECT at 1:113"}}
It throws an error because apparently log.message is of type "keyword" however this does not exist in the index mapping.
I thought this maybe an issue with the "target": "log" so ive tried changing this to something arbitrary like "my_parsed_message" or "m_log" or "mlog" and i get the same error for all of them.
{"type":"mapper_parsing_exception","reason":"failed to parse field
[mlog.message] of type [keyword] in document with id
'J5KlDHUB_yo5bfXcn2LE'. Preview of field's value: '{log_type=cron,
status=end}'","caused_by":{"type":"illegal_state_exception","reason":"Can't
get text on a START_OBJECT at 1:217"}}
Elastic version: 7.9.2
The problem is that some of your JSON messages contain a message field that is sometimes a simple string and other times a nested JSON object (like in the case you're showing in your question).
After this index was created, the very first message that was parsed was probably a string and hence the mapping has been modified to add the following field (line 10553):
"mlog": {
"properties": {
...
"message": {
"type": "keyword",
"ignore_above": 1024
},
}
}
You'll find the same pattern for my_parsed_message (line 10902), my_parsed_logs (line 10742), etc...
Hence the next message that comes with message being a JSON object, like
{"message":{"log_type":"cron","status":"start"}, ...
will not work because it's an object, not a string...
Looking at the fields of your custom JSON, it seems you don't really have the control over either their taxonomy (i.e. naming) or what they contain...
If you're serious about willing to search within those custom fields (which I think you are since you're parsing the field, otherwise you'd just store the stringified JSON), then I can only suggest to start figuring out a proper taxonomy in order to make sure that they all get a standard type.
If all you care about is logging your data, then I suggest to simply disable the indexing of that message field. Another solution is to set dynamic: false in your mapping to ignore those fields, i.e. not modify your mapping.

NLog: LayoutRenderer cannot be found: 'aspnet-user-identity

I try to implement NLog into my .NET Core Api web service.
I want to log to an Oracle database. All works well through an nlog.config XML file.
But the goal is to implement NLog config into appsettings.json and here problem occurs.
I get the error set in title:
LayoutRenderer cannot be found: 'aspnet-user-identity
My config file is like this :
"NLog": {
"autoReload": true,
"throwConfigExceptions": true,
"internalLogLevel": "info",
"internalLogFile": "c:/app/log/dev/internal-appsetting-nlog.txt",
"extensions": {
"NLog.Extensions.Logging": {
"assembly": [
"NLog.Extensions.Logging",
"NLog.Web.AspNetCore"
]
}
},
"variables": {
"var_logdir": "c:/app/log/dev"
},
"default-wrapper": {
"type": "AsyncWrapper",
"overflowAction": "Block"
},
"targets": {
"all-file": {
"type": "File",
"fileName": "${var_logdir}/nlog-all-${shortdate}.log",
"layout": {
"type": "JsonLayout",
"Attributes": [
{
"name": "timestamp",
"layout": "${date:format=o}"
},
{
"name": "level",
"layout": "${level}"
},
{
"name": "logger",
"layout": "${logger}"
},
{
"name": "message",
"layout": "${message:raw=true}"
},
{
"name": "properties",
"encode": false,
"layout": {
"type": "JsonLayout",
"includeallproperties": "true"
}
}
]
}
},
"db": {
"type": "Database",
"commandText": "INSERT INTO logtable (LOGLEVEL,LOGGER,MESSAGE,MACHINENAME,USERNAME,CALLSITE, THREADID,EXCEPTIONMESSAGE,STACKTRACE,SESSIONID) VALUES (:pLEVEL,:pLOGGER,:pMESSAGE,:pMACHINENAME, :pCALLSITE,:pTHREADID,:pEXCEPTIONMESSAGE,:pSTACKTRACE)",
"parameters": [
{
"name": "#pLEVEL",
"layout": "${level}"
},
{
"name": "#pLOGGER",
"layout": "${logger}"
},
{
"name": "#pMESSAGE",
"layout": "${message}"
},
{
"name": "#pMACHINENAME",
"layout": "${machinename}"
},
{
"name": "#pUSERNAME",
"layout": "${aspnet-user-identity}"
},
{
"name": "#pCALLSITE",
"layout": "${callsite:filename=true}"
},
{
"name": "#pTHREADID",
"layout": "${threadid}"
},
{
"name": "#pEXCEPTIONMESSAGE",
"layout": "${exception}"
},
{
"name": "#pSTACKTRACE",
"layout": "${stacktrace}"
},
{
"name": "#pSESSIONID",
"layout": "${aspnet-sessionid}"
}
],
"dbProvider": "Oracle.ManagedDataAccess.Client.OracleConnection, Oracle.ManagedDataAccess",
"connectionString": "xxxxxxxxxxxx"
}
},
"rules": [
{
"logger": "*",
"minLevel": "Trace",
"writeTo": "all-file"
},
{
"logger": "*",
"minLevel": "Trace",
"writeTo": "db"
},
{
"logger": "Microsoft.*",
"maxLevel": "Info",
"final": true
}
]
},
The internal debugger reports:
2019-10-09 16:48:48.6665 Info Adding target AsyncTargetWrapper(Name=all-file)
2019-10-09 16:48:48.7859 Warn Error when setting property 'Layout' on 'NLog.Targets.DatabaseParameterInfo' Exception: System.ArgumentException: LayoutRenderer cannot be found: 'aspnet-user-identity'. Is NLog.Web not included?
at NLog.Config.Factory`2.CreateInstance(String itemName)
at NLog.Layouts.LayoutParser.GetLayoutRenderer(ConfigurationItemFactory configurationItemFactory, String name)
at NLog.Layouts.LayoutParser.ParseLayoutRenderer(ConfigurationItemFactory configurationItemFactory, SimpleStringReader stringReader)
at NLog.Layouts.LayoutParser.CompileLayout(ConfigurationItemFactory configurationItemFactory, SimpleStringReader sr, Boolean isNested, String& text)
at NLog.Layouts.SimpleLayout.set_Text(String value)
at NLog.Internal.PropertyHelper.TryNLogSpecificConversion(Type propertyType, String value, Object& newValue, ConfigurationItemFactory configurationItemFactory)
at NLog.Internal.PropertyHelper.SetPropertyFromString(Object obj, String propertyName, String value, ConfigurationItemFactory configurationItemFactory)
Error occurs on ${aspnet-sessionid}. If I comment out both layout, everything works well.
I found different things on GitHub issue report but all I tried was a fail.
Could someone help?
The unknown aspnet-user-identity is probably an issue with your extensions:
"extensions": [
{ "assembly": "NLog.Extensions.Logging" },
{ "assembly": "NLog.Web.AspNetCore" }
],
Could you try the above suggestion?
P.S. Updated the wiki to include example of multiple "extensions"

Botframework AdaptiveCard is losing the “actions” node over directline

I'm using the botframework C# with directlinejs to build my bot.
Recently I've noticed that my bot stoped working and I just find out that the problem is that the directline/botconnector or something in the middle is losing the "actions" node of my AdaptiveCard over directline. Here is the message that my bot (server) is sending to the client (botconnector and directlinejs).
{
"$type": "Microsoft.Bot.Connector.Activity, Microsoft.Bot.Connector",
"type": "message",
"timestamp": "2018-04-19T17:57:14.565727+00:00",
"serviceUrl": "https://directline.botframework.com/",
"channelId": "directline",
"from": {
"$type": "Microsoft.Bot.Connector.ChannelAccount, Microsoft.Bot.Connector",
"id": "Toro#Q7xWzEtd_lk",
"name": "Toro Assistant"
},
"conversation": {
"$type": "Microsoft.Bot.Connector.ConversationAccount, Microsoft.Bot.Connector",
"id": "CfAgYrLQOuv9fDMPnfINDG"
},
"recipient": {
"$type": "Microsoft.Bot.Connector.ChannelAccount, Microsoft.Bot.Connector",
"id": "anonymous",
"name": "anonymous"
},
"text": "",
"attachments": {
"$type": "System.Collections.Generic.List`1[[Microsoft.Bot.Connector.Attachment, Microsoft.Bot.Connector]], mscorlib",
"$values": [
{
"$type": "Microsoft.Bot.Connector.Attachment, Microsoft.Bot.Connector",
"contentType": "application/vnd.microsoft.card.adaptive",
"content": {
"$type": "AdaptiveCards.AdaptiveCard, AdaptiveCards",
"type": "AdaptiveCard",
"version": "1.0",
"body": {
"$type": "System.Collections.Generic.List`1[[AdaptiveCards.AdaptiveElement, AdaptiveCards]], mscorlib",
"$values": [
{
"$type": "AdaptiveCards.AdaptiveTextBlock, AdaptiveCards",
"type": "TextBlock",
"text": "Não se preocupe, cadastrar um novo número é muito fácil. É só clicar no botão abaixo.\r\n\r\n"
}
]
},
"actions": {
"$type": "System.Collections.Generic.List`1[[AdaptiveCards.AdaptiveAction, AdaptiveCards]], mscorlib",
"$values": [
{
"$type": "AdaptiveCards.AdaptiveOpenUrlAction, AdaptiveCards",
"type": "imBack",
"url": "http://toroinvestimentos.com.br/minhaconta/emailecelular/edit?q=phone&token=token",
"title": "Falar com Assessor"
},
{
"$type": "AdaptiveCards.AdaptiveSubmitAction, AdaptiveCards",
"type": "imBack",
"data": "Mudar de assunto",
"title": "Mudar de assunto",
"image": "https://toro.azureedge.net/bot/icon_list_default.svg"
}
]
},
"style": "ToroCard1"
}
}
]
},
"entities": {
"$type": "System.Collections.Generic.List`1[[Microsoft.Bot.Connector.Entity, Microsoft.Bot.Connector]], mscorlib",
"$values": []
},
"replyToId": "CfAgYrLQOuv9fDMPnfINDG|0000011"
}
And here is the message that the botconnector/directlinejs is delivering to the client:
{
"type": "message",
"id": "CfAgYrLQOuv9fDMPnfINDG|0000012",
"timestamp": "2018-04-19T17:57:14.9161386Z",
"localTimestamp": "2018-04-19T17:57:14.6134302+00:00",
"channelId": "directline",
"from": {
"id": "Toro",
"name": "Toro Assistant"
},
"conversation": {
"id": "CfAgYrLQOuv9fDMPnfINDG"
},
"text": "",
"attachments": [
{
"contentType": "application/vnd.microsoft.card.adaptive",
"content": {
"type": "AdaptiveCard",
"version": "1.0",
"body": [
{
"type": "TextBlock",
"text": "Não se preocupe, cadastrar um novo número é muito fácil. É só clicar no botão abaixo.\r\n\r\n"
}
],
"style": "ToroCard1"
}
}
],
"entities": [],
"replyToId": "CfAgYrLQOuv9fDMPnfINDG|0000011"
}
Note that the "Actions" node of my message was croped by something in the middle (botconnector or directlinejs, I don't know).
Could anyone help me ?
I'm using the following version o botframework libs:
Microsoft.Bot.Builder version="3.14.1.1"
Microsoft.Bot.Connector" version="3.14.1.1"
"type": "imBack" is not a valid AdaptiveAction type.
For AdaptiveOpenUrlAction it should be "Action.OpenUrl" https://github.com/Microsoft/AdaptiveCards/blob/6700649f154cf12da6d02e063325b396026993c6/source/dotnet/Library/AdaptiveCards/AdaptiveOpenUrlAction.cs#L24
For AdaptiveSubmitAction it should be “Action.Submit” https://github.com/Microsoft/AdaptiveCards/blob/6700649f154cf12da6d02e063325b396026993c6/source/dotnet/Library/AdaptiveCards/AdaptiveSubmitAction.cs#L22
I did a test with the following sample code to send AdaptiveCard, and in my directjs client, I can find the actions node in message entity. So I suggested for sharing your code in my earlier comment.
In my bot application:
var replymes = context.MakeMessage();
AdaptiveCard card = new AdaptiveCard();
// Add text to the card.
card.Body.Add(new TextBlock()
{
Text = "This is an Adaptive Card",
Size = TextSize.Large,
Weight = TextWeight.Bolder
});
// Add buttons to the card.
card.Actions.Add(new OpenUrlAction()
{
Url = "http://foo.com",
Title = "Link1"
});
// Create the attachment.
Attachment attachment = new Attachment()
{
ContentType = AdaptiveCard.ContentType,
Content = card
};
replymes.Attachments.Add(attachment);
Console.WriteLine(card.ToString());
await context.PostAsync(replymes);
In directlinejs client, message entity contains actions node:
Besides, as Eric Dahlvang mentioned, "type": "imBack" is not a valid AdaptiveAction type. If we explicitly specify the type to "imBack", like below:
card.Actions.Add(new OpenUrlAction()
{
Url = "http://foo.com",
Title = "Link1",
Type= "imBack"
});
The actions node will miss in message entity:

Ruby json2csv fails

using
C:\RubyLearnToProgram>json2csv convert sample.json
results in following error:
Converting sample.json error: undefined method `keys' for
"PREPARED":String
The sample.json:
{
"preparationState": "PREPARED",
"consumerUsageStorageInstruction": {
"de": "gebruik",
"fr": "gebruik",
"en": "gebruik"
},
"npc": "1234",
"drainedWeight": "320.0GR",
"isBaseUnit": "true",
"packagingMarkedExpirationDateType": "BEST_BEFORE_DATE",
"numberOfServingsPerPackage": "100",
"functionalName": "Babbelaars",
"preparationInstructions": {
"de": "koken",
"fr": "koken",
"en": "koken"
},
"brandName": "Test lets try this one",
"manufacturerGln": "5060263871002",
"packagingRefuseObligationName": "ARA",
"gln": "5060263871002",
"height": "129.0MM",
"percentageOfAlcoholByVolume": "0.0",
"dailyValueIntakeReference": "10 a day keeps the doctor away",
"allergenStatement": {
"de": "Enthalt: Milch (Laktose)",
"fr": "Contient: lait de vache (lactose)",
"en": "Bevat: koemelk (lactose)"
},
"isPackagingMarkedReturnable": "false",
"netContent": "325.0GR",
"regulatedProductName": "Babbelaars",
"itemThumbnail": "/a/16257a91e7c42b64fdc7ad91292dce85/babbelaars.png",
"packages": [
{
"height": "100.0CM",
"containedGtin": "90000000000003",
"gtin": "91000000000002",
"width": "50.0CM",
"netContent": "100.0EA",
"packageUnitIndicator": "CASE",
"depth": "60.0CM",
"containedAmount": "10",
"grossWeight": "109.0KG"
}
],
"isVariableUnit": "false",
"taxRate": "STANDARD",
"width": "67.0MM",
"targetMarket": "528",
"gpc": "10000005",
"nameOfInformationProvider": "LANSA",
"depth": "67.0MM",
"grossWeight": "350.0GR",
"nutrients": [{
"percentageOfDailyValueIntake":"0.1","measurementPrecision":"APPROXIMATELY","quantityContainedServingSize":"1.0","quantityContainedHouseHoldServingSize":"10.0","quantityContainedUnit":"GR","nutrientTypeCode":"CHOAVL-"
},{
"percentageOfDailyValueIntake":"0.2","measurementPrecision":"APPROXIMATELY","quantityContainedServingSize":"2.0","quantityContainedHouseHoldServingSize":"20.0","quantityContainedUnit":"E14","nutrientTypeCode":"ENER-"
},{
"percentageOfDailyValueIntake":"0.3","measurementPrecision":"APPROXIMATELY","quantityContainedServingSize":"3.0","quantityContainedHouseHoldServingSize":"30.0","quantityContainedUnit":"GR","nutrientTypeCode":"FASAT"
},{
"percentageOfDailyValueIntake":"0.4","measurementPrecision":"APPROXIMATELY","quantityContainedServingSize":"4.0","quantityContainedHouseHoldServingSize":"40.0","quantityContainedUnit":"KJO","nutrientTypeCode":"ENER-"
},{
"percentageOfDailyValueIntake":"0.5","measurementPrecision":"APPROXIMATELY","quantityContainedServingSize":"5.0","quantityContainedHouseHoldServingSize":"50.0","quantityContainedUnit":"GR","nutrientTypeCode":"FIBTG"
},{
"percentageOfDailyValueIntake":"0.6","measurementPrecision":"APPROXIMATELY","quantityContainedServingSize":"6.0","quantityContainedHouseHoldServingSize":"60.0","quantityContainedUnit":"GR","nutrientTypeCode":"PRO-"
},{
"percentageOfDailyValueIntake":"0.7","measurementPrecision":"APPROXIMATELY","quantityContainedServingSize":"7.0","quantityContainedHouseHoldServingSize":"70.0","quantityContainedUnit":"GR","nutrientTypeCode":"FAT"
},{
"percentageOfDailyValueIntake":"0.8","measurementPrecision":"APPROXIMATELY","quantityContainedServingSize":"8.0","quantityContainedHouseHoldServingSize":"80.0","quantityContainedUnit":"GR","nutrientTypeCode":"SUGAR-"
},{
"percentageOfDailyValueIntake":"0.9","measurementPrecision":"APPROXIMATELY","quantityContainedServingSize":"9.0","quantityContainedHouseHoldServingSize":"90.0","quantityContainedUnit":"GR","nutrientTypeCode":"SALTEQ"
}],
"ingredientStatement": {
"de": "Zucker, Glukosesirup, Zuckersirup, Butter (3%), Salz.",
"fr": "sucre, sirop de glucose, sirop de sucre, beurre (3%), sel.",
"en": "sugar, glucose syrup, sugar syrup, butter (3%), salt."
},
"isItemReturnable": "false",
"shortDescription": {
"en": "Babbelaars"
},
"allergens": [
{
"allergenTypeCode": "BB",
"levelOfContainment": "CONTAINS"
}
],
"contactName": "LANSAEU1169",
"startAvailabilityDate": "2015-05-19",
"myattribute": "MyPostedEach",
"minimumLifespanFromTimeOfArrival": "8",
"householdServingSize": "household serving size",
"isDispatchUnit": "false",
"isBasePriceDeclarationRelevant": "false",
"allergenSpecificationAgency": "EU",
"isOrderableUnit": "false",
"allergenSpecificationName": "1169/2011",
"additives": [
{
"additiveName": "additive sample name",
"levelOfContainment": "CONTAINS"
}
],
"minimumLifespanFromTimeOfProduction": "10",
"baseUnitIndicator": "BASE_UNIT_OR_EACH",
"effectiveDate": "2015-05-19",
"servingSize": "GR",
"countryOfOrigin": "528",
"preparationType": "PRESSURE_COOKING",
"supplierItemNumber": "324324234",
"gtin": "90000000000003",
"pallets": [
{
"palletTypeCode": "37",
"height": "10.0MR",
"optionalGtin": "92000000000001",
"containedGtin": "91000000000002",
"palletTermsAndConditions": "3",
"width": "250.0CM",
"quantityOfLayersPerPallet": "5",
"palletUnitIndicator": "PALLET",
"depth": "120.0CM",
"containedAmount": "50",
"grossWeight": "1200.0KG",
"logisticsUnitStackingFactor": "10"
}
],
"barCodeType": "EAN_UCC_13_SYMBOL",
"nameOfManufacturer": "LANSA",
"compulsoryAdditivesLabelInformation": {
"de": "comp add label",
"fr": "comp add label",
"en": "comp add label"
},
"isInvoiceUnit": "false",
"isConsumerUnit": "true",
"packagingTypeCode": "CAN",
"communicationAddress": "Leliehof 26, 4841 ML PRINSENBEEK",
"itemImage": "/a/d59455129b92fb21dd00a97746a81439/babbelaars.png",
"isPackagingExemptFromRefuseObligation": "NOT_APPLICABLE"
}
Could you help me with this, I'm trying to get all keys and nested keys to create a dynamic conversion of values. Thanks in advance, Greetings Danny

Resources