Bot Composer and BotBuilder FacebookAdapter resulting in "Object reference not set to an instance of an object" - botframework

I am using MS Bot Composer combined with BotBuilder Facebook Adapter in order to post my bot following the "new" rules of Facebook Workplace - important to remember that I've already tried to use this directly on Facebook Messenge, same situation applies.
The integration and connection works fine, the problem now is to understand how to post anything apart from text.
An example below of an attempt to get the correct template
# channelData
-```{
"attachment":{
"type":"template",
"payload":{
"template_type":"button",
"text":"What do you want to do next?",
"buttons":[
{
"type":"web_url",
"url":"https://www.messenger.com",
"title":"Visit Messenger"
}
]
}
}
}```
And then, the bot's answer:

If you go the channel data route, the Facebook adapter expects you to provide a full FacebookMessage object as the channel data. That's one level up from the JSON you've provided, so it would look like this:
{
"message":{
"attachment":{
"type":"template",
"payload":{
"template_type":"button",
"text":"What do you want to do next?",
"buttons":[
{
"type":"web_url",
"url":"https://www.messenger.com",
"title":"Visit Messenger"
}
]
}
}
}
}
However, that won't work because it's missing the other properties a FacebookMessage needs, like sender. Rather than trying to populate the full Facebook message, it may be easier to just go the attachment route instead of the channel data route. You can see how to do that in the Facebook adapter sample:
private static Attachment CreateTemplateAttachment(string filePath)
{
var templateAttachmentJson = File.ReadAllText(filePath);
var templateAttachment = new Attachment()
{
ContentType = "template",
Content = JsonConvert.DeserializeObject(templateAttachmentJson),
};
return templateAttachment;
}
In Composer, you can set the activity's attachments instead of its channel data. Just set the content type to "template" and the content to the payload of the attachment you have now, using the Facebook adapter sample's resources as a guide:
# attachment
- ```
{
"contentType": "template",
"content": {
"template_type": "button",
"text": "What do you want to do next?",
"buttons": [
{
"type": "web_url",
"url": "https://www.messenger.com",
"title": "Visit Messenger"
}
]
}
}
```
You can see how the Facebook adapter treats both channel data and attachments by having a look at the FacebookHelper class.

Related

GraphQL java: return a partial response and inform a user about it

I have a SpringBoot application that uses GraphQL to return data to a request.
What I have
One of my queries returns a list of responses based on a list of ids supplied. So my .graphqls file is a follows:
type Query {
texts(ids: [String]): [Response]
}
type Response {
id: String
text: String
}
and the following are request & response:
Request
texts(ids:["id 1","id 2"]){
id
text
}
Response
{
"data": [
{
"id": "id 1",
"text": "Text 1"
},
{
"id": "id 2",
"text": "Text 2"
}
]
}
At the moment, if id(s) is/are not in aws, then exception is thrown and the response is an error block saying that certain id(s) was/were not found. Unfortunately, the response for other ids that were found is not displayed - instead the data block returns a null. If I check wether data is present in the code via ssay if/else statment, then partial response can be returned but I will not know that it is a partial response.
What I want to happen
My application fetches the data from aws and occasionally some of it may not be present, meaning that for one of the supplied ids, there will be no data. Not a problem, I can do checks and simply never process this id. But I would like to inform a user if the response I returned is partial (and some info is missing due to absence of data).
See example of the output I want at the end.
What I tried
While learning about GraphQL, I have encountered an instrumentation - a great tool for logging. Since it goes through all stages of execution, I thought that I can try and change the response midway - the Instrumentation class has a lot of methods, so I tried to find the one that works. I tried to make beginExecution(InstrumentationExecutionParameters parameters) and instrumentExecutionResult(ExecutionResult executionResult, InstrumentationExecutionParameters parameters) to work but neither worked for me.
I think the below may work, but as comments suggests there are parts that I failed to figure out
#Override
public GraphQLSchema instrumentSchema(GraphQLSchema schema, InstrumentationExecutionParameters parameters) {
String id = ""; // how to extract an id from the passed query (without needing to disect parameters.getQuery();
log.info("The id is " + id);
if(s3Service.doesExist(id)) {
return super.instrumentSchema(schema, parameters);
}
schema.transform(); // How would I add extra field
return schema;
}
I also found this post that seem to offer more simpler solution. Unfortunately, the link provided by host does not exist and link provided by the person who answered a question is very brief. I wonder if anyone know how to use this annotation and maybe have an example I can look at?
Finally, I know there is DataFetcherResult which can construct partial response. The problem here is that some of my other apps use reactive programming, so while it will be great for Spring mvc apps, it will not be so great for spring flux apps (because as I understand it, DataFetcherResult waits for all the outputs and as such is a blocker). Happy to be corrected on this one.
Desired output
I would like my response to look like so, when some data that was requested is not found.
Either
{
"data": [
{
"id": "id 1",
"text": "Text 1"
},
{
"id": "id 2",
"text": "Text 2"
},
{
"id": "Non existant id",
"msg": "This id was not found"
}
]
}
or
{
"error": [
"errors": [
{
"message": "There was a problem getting data for this id(s): Bad id 1"
}
]
],
"data": [
{
"id": "id 1",
"text": "Text 1"
},
{
"id": "id 2",
"text": "Text 2"
}
]
}
So I figured out one way of achieving this, using instrumentation and extension block (as oppose to error block which is what I wanted to use initially). The big thanks goes to fellow Joe, who answered this question. Combine it with DataFetchingEnviroment (great video here) variable and I got the working solution.
My instrumentation class is as follows
public class CustomInstrum extends SimpleInstrumentation {
#Override
public CompletableFuture<ExecutionResult> instrumentExecutionResult(
ExecutionResult executionResult,
InstrumentationExecutionParameters parameters) {
if(parameters.getGraphQLContext().hasKey("Faulty ids")) {
Map<Object, Object> currentExt = executionResult.getExtensions();
Map<Object, Object> newExtensionMap = new LinkedHashMap<>();
newExtensionMap.putAll(currentExt == null ? Collections.emptyMap() : currentExt);
newExtensionMap.put("Warning:", "No data was found for the following ids: " + parameters.getGraphQLContext().get("Faulty ids").toString());
return CompletableFuture.completedFuture(
new ExecutionResultImpl(
executionResult.getData(),
executionResult.getErrors(),
newExtensionMap));
}
return CompletableFuture.completedFuture(
new ExecutionResultImpl(
executionResult.getData(),
executionResult.getErrors(),
executionResult.getExtensions()));
}
}
and my DataFetchingEnviroment is in my resolver:
public CompletableFuture<List<Article>> articles(List<String> ids, DataFetchingEnvironment env) {
List<CompletableFuture<Article>> res = new ArrayList<>();
// Below's list would contain the bad ids
List<String> faultyIds = new ArrayList<>();
for(String id : ids) {
log.info("Getting article for id {}",id);
if(s3Service.doesExist(id)) {
res.add(filterService.gettingArticle(id));
} else {
faultyIds.add(id);// if data doesn't exist then id will not be processed
}
}
// if we have any bad ids, then we add the list to the context for instrumentations to pick it up, right before returning a response
if(!faultyIds.isEmpty()) {
env.getGraphQlContext().put("Faulty ids", faultyIds);
}
return CompletableFuture.allOf(res.toArray(new CompletableFuture[0])).thenApply(item -> res.stream()
.map(CompletableFuture::join)
.collect(Collectors.toList()));
}
You can obviously separate error related ids to different contexts but for my simple case, one will suffice. I however still interested in how can the same results be achieved via error block, so i will leave this question hanging for a bit before accepting this as a final answer.
My response looks as follows now:
{
"extensions": {
"Warning:": "No data was found for the following ids: [234]"
},
"data": { ... }
My only concern with this approach is security and "doing the right thing" - is this correct thing to do, adding something to the context and then using instrumentation to influence the response? Are there any potential security issues? If someone know anything about it and could share, it will help me greatly!
Update
After further testing it appears if exception is thrown it will still not work, so it only works if you know beforehand that something goes wrong and add appropriate exception handling. Cannot be used with try/catch block. So I am a half step back again.

Use Postman to test Appsync Subscription

I have been able to successfully execute Appsync GraphQL queries and mutations from Postman. However, i'm struggling to connect to subscriptions which are websocket urls.
How can I achieve the same ?
Since Postman supports WebSockets testing GraphQL subscriptions is achievable as well. Such a testing requires two steps:
connection to a server,
sending a start message.
Establishing a connection:
Create a new WebSocket request.
Put your server URL ws:// or wss://.
Add custom header parameter Sec-WebSocket-Protocol: graphql-ws. Other headers may depend on your server configuration.
Press the "Connect" button.
When the connection is established we may start a subscription.
In the "New message" field put the command.
Press the "Send" button.
The start message should look like this:
{
"id":"1",
"payload": {
"operationName": "MySubscription",
"query": "subscription MySubscription {
someSubscription {
__typename
someField1
someField2 {
__typename
someField21
someField22
}
}
}",
"variables": null
},
"type": "start"
}
operationName is just the name of your subscription, I guess it's optional. And someSubscription must be a subscription type from your schema.
query reminds regular GraphQL syntax with one difference:
__typename keyword precedes every field list.
For example, the query from the payload in regular syntax looks like the following:
subscription MySubscription {
someSubscription {
someField1
someField2 {
someField21
someField22
}
}
}
Example message with parameters (variables):
{
"id":"1",
"payload": {
"operationName": "MySubscription",
"query": "subscription MySubscription($param1: String!) {
someSubscription((param1: $param1)) {
__typename
someField
}
}",
"variables": {
"param1": "MyValue"
}
},
"type": "start"
}
It also reminds regular GraphQL syntax as described above.
variables is an object with your parameters.
#Vladimir's answer is spot on. Adding a few notes for folks still having trouble.
Full document here # https://docs.aws.amazon.com/appsync/latest/devguide/real-time-websocket-client.html
Step 1 - establish connection:
make sure to base64 encode values in "header" and "payload" querystrings
header example:
{
"host":"example1234567890000.appsync-api.us-east-1.amazonaws.com",
"x-api-key":"da2-12345678901234567890123456"
}
payload: You can pass in empty payload
{}
Step 2 - register subscription:
Include the authorization in the message. Escape line feeds properly "\n" throws an error but "\\n" works. it throws the following error - misleading.
Don't forget to stringify value in "data" field.
{
"type": "error",
"payload": {
"errors": [
{
"errorType": "UnsupportedOperation",
"message": "unknown not supported through the realtime channel"
}
]
}
}
{
"id": "2",
"payload": {
"data": "{\"query\":\"subscription onCreateMessage { changeNotification{ __typename changeType from } }\",\"variables\":{}}",
"extensions":{
"authorization":{
"host":"example1234567890000.appsync-api.us-east-1.amazonaws.com",
"x-api-key":"da2-12345678901234567890123456"
}
}
},
"type": "start"
}

Can I create extended properties in Google People API and Task API?

I have added an extended property to a Google calendar entry and been able to read it back successfully. The format of the json is like this:
"extendedProperties": {
"private": {
"MyPropertyName": "yes"
}
},
I want to do the same thing to created Task entries and contact entries (via the People API). With the People API, trying to create the entry results in http 400. With the Task API, it accepts the json, but the property is not returned when I retrieve the task.
Is it possible to do what I want with the current versions of the People and Task API?
In People API extended properties are called ClientData
The json structure of the resouce is:
{
"metadata": {
object (FieldMetadata)
},
"key": string,
"value": string
}
with FieldMetadata:
{
"primary": boolean,
"sourcePrimary": boolean,
"verified": boolean,
"source": {
object (Source)
}
}

office 365 outlook Graph api custom email message header

In order to organize emails into single thread GMail requires custom headers to be set within incoming message. Is there any way to set custom headers when sending Office O365 Outlook message through the Graph API? The documentation states about custom headers option through the InternetMessageHeaders property, however it is required to start header name with 'x-' appendix, which makes it not usable. For example, this message is supposed to set some custom headers, however, due to the 'x-' prefix limitation, this can't be used for organizing GMail messages:
POST https://graph.microsoft.com/v1.0/me/sendMail
Content-type: application/json
{
"message": {
"subject": "9/9/2018: concert",
"body": {
"contentType": "HTML",
"content": "The group represents Nevada."
},
"toRecipients": [
{
"emailAddress": {
"address": "AlexW#contoso.OnMicrosoft.com"
}
}
],
"internetMessageHeaders":[
{
"name":"x-custom-header-group-name",
"value":"Nevada"
},
{
"name":"x-custom-header-group-id",
"value":"NV001"
}
]
}
}
https://learn.microsoft.com/en-us/graph/api/user-sendmail?view=graph-rest-1.0&tabs=http
Is there any way to specify custom headers: 'References', 'In-Reply-To' without the prefix 'x-'?
https://gsuiteupdates.googleblog.com/2019/03/threading-changes-in-gmail-conversation-view.html
When trying to set custom email headers without 'x-' prefix, the Graph API returns following error message:
{
"error": {
"code": "InvalidInternetMessageHeader",
"message": "The internet message header name 'References' should start with 'x-' or 'X-'.",
"innerError": {
"request-id": "441e21b7-d4ca-47d3-957a-a72bcc854a67",
"date": "2019-12-10T14:28:35"
}
}
}
One workaround might be to use the Extended properties to set those values eg the In-Reply-to https://learn.microsoft.com/en-us/office/client-developer/outlook/mapi/pidtaginreplytoid-canonical-property should look something like
"singleValueExtendedProperties": [
{
"id":"String 0x1042",
"value":"342342343234#domain.com"
}
]

GraphQL Move a child property into parent

I'm looking into GraphQL and got to a question I'm not even sure how to look for in the docs. Probably this isn't even supposed to work, but I'll ask anyway.
I have this query:
query {
organization(login: "facebook") {
repositories(first: 5) {
items: edges {
repo: node {
name
owner {
login
}
}
}
}
}
}
Now, in the response I would like a way to place the login next to name, instead of nested in owner login.
So in the response instead of:
{
...
"name": "react-native",
"owner": {
"login": "facebook"
}
}
I would like to have:
{
...
"name": "react-native",
"ownerName": "facebook"
}
Thank you.
From the specification this is not possible. The response has to be shaped in the way the object types are shaped. There is a project called GraphQL Lodash that gives you a new directive to modify the results. It can be helpful here but it is rather experimental and IMO very undogmatic.

Resources