Related
I have made an app for Teams that I want to use to display an adaptive card to the user when they pick an item from the list of search results. In order for this to happen, I need to trigger some code after the user selects a result. This works as expected from the Teams client, as well as in the browser, but from native mobile Teams app, the code is not triggered when selecting an item from the list of results.
const preview = CardFactory.heroCard( obj.package.name );
preview.content.tap = { type: 'invoke', value: { description: obj.package.description } };
The following pictures show the app working in a browser on the computer:
The list of results from the browser on PC
The expected adaptive card showing correctly on browser
And this is how it looks from the mobile perspective:
The list of results from mobile app
The result of selecting the same item from the list
The code used to display this has not been modified, except providing a bot to host it, and was found from Microsoft's bot samples on GitHub:
https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/typescript_nodejs/50.teams-messaging-extensions-search
The code in question looks as follows:
export class TeamsMessagingExtensionsSearchBot extends TeamsActivityHandler {
public async handleTeamsMessagingExtensionQuery( context: TurnContext, query: any ): Promise<any> {
const searchQuery = query.parameters[ 0 ].value;
const response = await axios.get( `http://registry.npmjs.com/-/v1/search?${ querystring.stringify( { text: searchQuery, size: 8 } ) }` );
const attachments = [];
response.data.objects.forEach( ( obj: any ) => {
const heroCard = CardFactory.heroCard( obj.package.name );
const preview = CardFactory.heroCard( obj.package.name );
preview.content.tap = { type: 'invoke', value: { description: obj.package.description } };
const attachment = { ...heroCard, preview };
attachments.push( attachment );
} );
return {
composeExtension: {
attachmentLayout: 'list',
attachments,
type: 'result'
}
};
}
public async handleTeamsMessagingExtensionSelectItem( context: TurnContext, obj: any ): Promise<any> {
return {
composeExtension: {
attachmentLayout: 'list',
attachments: [ CardFactory.thumbnailCard( obj.description ) ],
type: 'result'
}
};
}
}
Is this expected?
Thanks
Edit: Adding the manifest JSON used here:
{
"$schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.5/MicrosoftTeams.schema.json",
"manifestVersion": "1.5",
"version": "1.0.0",
"id": "9211fa66-f930-414d-861a-40f18f7f1490",
"packageName": "com.teams.sample.teamsmessagingextensionssearch",
"developer": {
"name": "teamsStartNewThreadInChannel",
"websiteUrl": "https://www.microsoft.com",
"privacyUrl": "https://www.teams.com/privacy",
"termsOfUseUrl": "https://www.teams.com/termsofuser"
},
"icons": {
"outline": "icon-outline.png",
"color": "icon-color.png"
},
"name": {
"short": "Search Messaging Extension",
"full": "Microsoft Teams Search Based Messaging Extension"
},
"description": {
"short": "Sample demonstrating a Search Based Messaging Extension",
"full": "Sample Search Messaging Extension built with the Bot Builder SDK"
},
"accentColor": "#FFFFFF",
"bots": [
{
"botId": "9211fa66-f930-414d-861a-40f18f7f1490",
"scopes": [
"personal",
"groupchat",
"team"
],
"supportsFiles": false,
"isNotificationOnly": false
}
],
"composeExtensions": [
{
"botId": "9211fa66-f930-414d-861a-40f18f7f1490",
"canUpdateConfiguration": true,
"commands": [
{
"id": "searchQuery",
"context": [
"compose",
"commandBox"
],
"description": "Test command to run query",
"title": "Search",
"type": "query",
"parameters": [
{
"name": "searchQuery",
"title": "Search Query",
"description": "Your search query",
"inputType": "text"
}
]
}
]
}
],
"permissions": [
"identity",
"messageTeamMembers"
],
"validDomains": []
}
We are able to re-pro the issue at our end. Raised a bug.We are tracking the bug internally, we don't have ETA to share when it will be fixed. Will update once it is fixed.
I've been running into the following exception at runtime with the debugger trying to de-serialize data from my Algolia index for my Kotlin Android recipe app I am trying to create by using the Kotlinx.Serialization library. The app compiles and runs fine, but no results show on the UI.
kotlinx.serialization.json.JsonDecodingException: Unexpected JSON token at offset -1: Failed to parse 'int'.
JSON input: {"amount":1.5,"name":"green beans","original":"1.5 pounds of green beans","unit":"pounds","unitLong":"pounds","unitShort":"lbs"}
Now from the looks this exception, It looks like the de-serializer is getting confused try to de-serialize my Ingredients data class. How would I go about de-serializing it?.
Example JSON data that is being sent over.
{
"cuisine": "European",
"diet": "Vegetarian",
"difficulty": 2,
"dishType": "Dinner",
"duration": 30,
"durationUnit": "minutes",
"image": "https://c.recipeland.com/images/r/1396/12f9fc271d8f1bfac5f6_550.jpg",
"ingredients": [
{
"amount": 1.5,
"name": "green beans",
"original": "1.5 pounds of green beans",
"unit": "pounds",
"unitLong": "pounds",
"unitShort": "lbs"
},
{
"amount": 1,
"name": "onion",
"original": "1.5 medium onion",
"unit": "medium",
"unitLong": "medium",
"unitShort": "med"
},
{
"amount": 2,
"name": "garlic",
"original": "2 teaspoons of garlic",
"unit": "teaspoons",
"unitLong": "teaspoons",
"unitShort": "tsps"
},
{
"amount": 1,
"name": "olive oil",
"original": "1 teaspoon olive oil",
"unit": "teaspoon",
"unitLong": "teaspoon",
"unitShort": "tsps"
},
{
"amount": 1,
"name": "mushrooms",
"original": "1 cup mushrooms",
"unit": "cup",
"unitLong": "cup",
"unitShort": "cup"
},
{
"amount": 1,
"name": "cherry tomatoes",
"original": "1 cup cherry tomatoes",
"unit": "cup",
"unitLong": "cup",
"unitShort": "cup"
}
],
"name": "Green Beans with Mushrooms and Cherry Tomatoes",
"preparation": [
"Steam green beans until tender.",
"Drain and set aside. Sauté onion and garlic in a medium skillet coated with olive oil, until tender. About 2 to 3 minutes.",
"Add mushrooms and sauté until tender. Stir in green beans and tomotoes until heated."
],
"yield": 4,
"objectID": "0"
}
I have my data classes for a recipe set up as the following:
Recipe.kt
#IgnoreExtraProperties
#Serializable
data class Recipe(
var difficulty: Int = 0,
var dishType: String? = null,
var duration: Int = 0,
var durationUnit: String? = null,
var image: String? = null,
var diet: String? = null,
var cuisine: String? = null,
var name: String? = null,
var ingredients: List<Ingredient> = emptyList(),
var preparation: List<String> = emptyList(),
var yield: Int = 0
) {
Ingredient.kt
#Serializable
data class Ingredient(
var amount: Int = 0,
var name: String? = null,
var original: String? = null, // Original text of the ingredient
var unit: String? = null,
var unitLong: String? = null,
var unitShort: String? = null
)
This block of code I got from Algolia's getting started guide for InstantSearch Android that de-serializes the data from the index.
private val datasourceFactory = SearcherSingleIndexDataSource.Factory(searcher) { hit ->
hit.deserialize(Recipe.serializer()) // Problem line I assume
}
val pagedListConfig = PagedList.Config.Builder().setPageSize(50).build()
val recipes: LiveData<PagedList<Recipe>> =
LivePagedListBuilder(datasourceFactory, pagedListConfig).build()
val searchBox =
SearchBoxConnectorPagedList(searcher, listOf(recipes))
I've tried to manually create the object by using the following code, but I run into issues when trying to create the list of ingredients.
val dataSourceFactory = SearcherSingleIndexDataSource.Factory(searcher) { hit ->
Recipe(
hit.json.getPrimitive("difficulty").content.toInt(),
hit.json.getPrimitive("dishType").content,
hit.json.getPrimitive("duration").content.toInt(),
hit.json.getPrimitive("durationUnit").content,
hit.json.getPrimitive("image").content,
hit.json.getPrimitive("diet").content,
hit.json.getPrimitive("cuisine").content,
hit.json.getPrimitive("name").content,
listOf(
Ingredient(
hit.json.getPrimitive("amount").content.toInt(),
hit.json.getPrimitive("name").content,
hit.json.getPrimitive("original").content,
hit.json.getPrimitive("unit").content,
hit.json.getPrimitive("unitLong").content,
hit.json.getPrimitive("unitShort").content
)
),
hit.json.getArray("preparation").content.map { prep -> prep.content },
hit.json.getPrimitive("yield").content.toInt()
)
}
I'm not 100% sure if I'm properly creating the preparation property member correctly as well as the whole creating the list of ingredients has side-tracked me. Any help would be greatly be appreciated and I apologize for my first post on here being a long one. I've been going at this for a couple of days already and I'm stumped as to what to do next.
As you can see this line:
kotlinx.serialization.json.JsonDecodingException: Unexpected JSON token at offset -1: Failed to parse 'int'.
Here JsonDecodingException exception occur that's why it is not giving proper response. You must check your all data classes are same variable which is in JSON Object.
Here I found 1 issue in your data class, First check this JSON Reposne:
"amount": 1.5
and now check your data class, which has var amount: Int = 0
#Serializable
data class Ingredient(
var amount: Int = 0,
var name: String? = null,
var original: String? = null, // Original text of the ingredient
var unit: String? = null,
var unitLong: String? = null,
var unitShort: String? = null
)
Here JSON Object is in Float and you are storing in it Int, which may cause exception. Make sure all values in data class are proper.
Or for work around you just make String all variable in data class to check all response show proper, than after just convert them to Int, Float according to your requirements.
Initially, I declared a variable named detailItem with the data type Map. After that, I fill the variable with a variety of data, but when changing it occurs an error. For more details, see the code that I included below.
I've tried searching google and documentation, but I still haven't gotten the answer.
dart
Map detailItem;
detailItem = {
"item": {
'images': [],
'title': '',
'price': '',
'stock': '',
'description': '',
'rating': 4,
'author': {
'name': '',
'image': '',
'verified': true,
},
'review': [
]
}
};
detailItem['item']['images'] = [
"https://cms.dailysocial.id/wp-content/uploads/2017/12/4561bedaab1d7cef932f9d41cc091fee_game-mmorpg-iris-m-4.jpg",
"https://cnet1.cbsistatic.com/img/-8OMdJa3DGw53_Xxdoi7mc-ZWYg=/980x551/2018/04/26/da817b98-516f-4213-9fa8-959378b900e4/pubgmobilejp.jpg"
];
detailItem['item']['title'] = 'Fornite - Account LImited Edition';
detailItem['item']['price'] = '200.993';
detailItem['item']['stock'] = '1';
detailItem['item']['description'] =
'Best skins, Wukong, Love Ranger, and 4 others';
detailItem['item']['author']['name'] = 'Zeus God Store';
detailItem['item']['author']['image'] =
'https://i.pinimg.com/originals/17/9c/7c/179c7c77b5808a922f64de12479d4a64.jpg';
detailItem['item']['author']['verified'] = true;
//===== THIS IS ERROR OCCURS =====
detailItem['item']['review'][0]['name'] = 'Adit'; //this code is where an error occurs
detailItem['item']['review'][0]['message'] = 'Cool !';
detailItem['item']['review'][1]['name'] = 'Julian';
detailItem['item']['review'][1]['message'] = 'Bagus';
detailItem['item']['review'][2]['name'] = 'Ricky';
detailItem['item']['review'][2]['message'] = 'Keren';
I hope the contents of the variable from detailItem are like this:
dart
detailItem = {
"item": {
'images': [
"https://cms.dailysocial.id/wp-content/uploads/2017/12/4561bedaab1d7cef932f9d41cc091fee_game-mmorpg-iris-m-4.jpg",
"https://cnet1.cbsistatic.com/img/-8OMdJa3DGw53_Xxdoi7mc-ZWYg=/980x551/2018/04/26/da817b98-516f-4213-9fa8-959378b900e4/pubgmobilejp.jpg"
],
'title': 'Fornite - Account LImited Edition',
'price': '200.993',
'stock': '1',
'description': 'Best skins, Wukong, Love Ranger, and 4 others',
'rating': 4,
'author': {
'name': 'Zeus God Store',
'image': 'https://i.pinimg.com/originals/17/9c/7c/179c7c77b5808a922f64de12479d4a64.jpg',
'verified': true,
},
'review': [
{
'name' : 'Adit',
'message' : 'Cool !',
},
{
'name' : 'Julian',
'message' : 'Bagus',
},
{
'name' : 'Ricky',
'message' : 'Keren',
}
]
}
};
But I get an error in the code:
detailItem ['item'] ['review'] [0] ['name'] = 'Adit';
Error : "Invalid value: Valid value range is empty: 0"
Can a variable with a map data type not be filled with:
x [integer] = value ??
I just want to fill in the JSON data that I got from the API and then put it in a variable.
Update: Also u can do it 1 line
detailItem['item']['review'] = (detailItem['item']['review'] as List)
..addAll([{
'name': 'Ricky',
'message': 'Keren',
}, {..}, {..} ]);
List<dynamic> nameMapList = detailItem['item']['review'];
nameMapList.addAll([
{'name': 'Adit', 'message': 'Cool !'},
{'name': 'Julian', 'message': 'Bagus'},
]);
detailItem['item']['review'] = nameMapList;
print(detailItem['item']['review']);
Result >> [{name: Adit, message: Cool !}, {name: Julian, message: Bagus}]
nameMapList.add(
{'name': 'Ricky', 'message': 'Keren'},
);
detailItem['item']['review'] = nameMapList;
print(detailItem['item']['review']);
Result >> [{name: Adit, message: Cool !}, {name: Julian, message: Bagus}, {name: Ricky, message: Keren}]
You can just do:
detailItem['item']['review'] = [
{
'name' : 'Adit',
'message' : 'Cool !',
},
{
'name' : 'Julian',
'message' : 'Bagus',
},
{
'name' : 'Ricky',
'message' : 'Keren',
}
];
And then you will get in the review node the following:
review: [{name: Adit, message: Cool !}, {name: Julian, message: Bagus}, {name: Ricky, message: Keren}]}}
List<Map<String, String>> abc = detailItem['item']['review'];
print(abc.first["name"] == "Adit");
print(detailItem['item']['review']);
I want to send a post request with loopback "invokeStaticMethod".
Please help me how to do it.
I want to send a POST API request to below url:
localhost:3000/api/user/id/unblock With parameter {"userId", "blockId"}
Please let me know how can I send a POST request with Loopback
You could create a remote method like this:
User.unblock = function(id, userId, blockId, callback) {
var result;
// TODO
callback(null, result);
};
Then, the remote method definition in the json file could look like this:
"unblock": {
"accepts": [
{
"arg": "id",
"type": "string",
"required": true,
"description": "",
"http": {
"source": "path"
}
},
{
"arg": "userId",
"type": "string",
"required": false,
"description": "",
"http": {
"source": "form"
}
},
{
"arg": "blockId",
"type": "string",
"required": false,
"description": "",
"http": {
"source": "form"
}
}
],
"returns": [
{
"arg": "result",
"type": "object",
"root": false,
"description": ""
}
],
"description": "",
"http": [
{
"path": "/:id/unblock",
"verb": "post"
}
]
}
Then your remote method would look like this:
You could play around with function arguments and use one body argument instead of 2 form arguments and read the data from there, although I believe that if there are only 2 additional parameters it's better to put them separately. But it depends on your approach.
I believe this is what you are looking for...
https://loopback.io/doc/en/lb3/Adding-remote-methods-to-built-in-models.html
In your case, it should look something like this...
module.exports = function(app) {
const User = app.models.User;
User.unblock = function(userId, blockId, cb) {
... <Your logic goes here> ...
cb(null, result);
};
User.remoteMethod('unblock', {
accepts: [{arg: 'userId', type: 'string'}, {arg: 'blockId', type: 'string'}],
returns: {arg: 'result', type: 'string'}
});
This json object can be very very large sometimes, I would like to parse it with GSON but I do not quite get how the format of my java objects should be to parse them.
What would really help me are some very contextual examples, given this object, how would I form my java model objects to hold the data in a gson.fromJSON method? My current objects get filled with "null"
I explain the simplicity of the object at the bottom
{
response: {
user_is_following: 0,
name: "Tennesee",
submitted_requests: 429,
completed_requests: 34,
request_types: {
c_id: 1064,
request_types: [
{
objectKey: {
id: 15699,
name: "Complaint",
has_custom_fields: 0,
disable_title: 0,
disable_description: 0,
force_private: 0,
image: null
}
},
{
objectKey: {
id: 15700,
name: "Compliment",
has_custom_fields: 0,
category_id: 605,
disable_title: 0,
disable_description: 0,
force_private: 0,
image: null
}
},
{
objectKey: {
id: 17574,
name: "Custom Fields, all of them",
has_custom_fields: 1,
disable_title: 0,
disable_description: 0,
force_private: 0,
image: null,
custom_fields: [
{
custom_field: {
id: "1663",
name: "I'm a text input",
description: "I'm a text input description",
type: "text",
required: 1,
is_public: 1,
options: [
]
}
},
{
custom_field: {
id: "1664",
name: "I'm a text input display only",
description: "I'm a text input display only description",
type: "display",
required: 0,
is_public: 0,
options: [
]
}
},
{
custom_field: {
id: "1665",
name: "I'm a checkbox",
description: "I'm a checkbox description",
type: "checkbox",
required: 0,
is_public: 1,
options: [
]
}
},
{
custom_field: {
id: "1666",
name: "I'm a single select",
description: "I'm a single select description",
type: "singleselect",
required: 1,
is_public: 0,
options: [
{
option: {
id: "3751",
name: "A 123 !###",
description: "A 123 !### description"
}
},
{
option: {
id: "3752",
name: "B ",
description: "B description"
}
},
{
option: {
id: "3753",
name: "C",
description: "C description"
}
},
{
option: {
id: "3754",
name: " D",
description: "D description"
}
}
]
}
},
}
],
s_types: [
],
categories: [
{
category: {
id: 618,
client: 1064,
name: "Abc",
gov_creator: 1841,
description: "",
parent: 607,
date_created: 1368137256,
image: null
}
},
{
category: {
id: 602,
client: 1064,
name: "Animal Control",
gov_creator: 2275,
description: "",
parent: null,
date_created: 1367878768,
image: null
}
},
}
],
assets: [
],
benchmark: 0.36078095436096
},
status: {
type: "success",
message: "Success",
code: 200,
code_message: "Ok"
}
}
}
The real meat is in the request_types key, the second one, which is a JSONArray. Each index contains an object, each object can contain a Custom Fields key which is a json array as well, which in some cases can contain an options json array.
I have models for all of these, for a different parsing paradigm, but not for GSON. I will need to use GSON now because of memory limitations
You will need lets say class A with one instance of class B in it.
public class A {
private B response;
}
then you will need class B to have 1 instance of class C
public class B {
private C request_types;
}
Then C would contains int c_id and array of a class D and a class for each of the other arrays as well. Then class D would contain a single object for class E called objectKey. Class E would contains all the fields under objectKey...
So on and so forth... you're right the JSON is crazy convoluted.