I'm trying to batch create a bunch of intents, and I want to assign an Input context
As far as I can see this should not need a session as it's just a string context name.
Like this in the GUI:
The API call I make creates the intent,
doesn't throw an error,
but I can't get the contexts to show up.
Is there some weird format to the contexts parameter?
I've exported the zip and looked at the JSON files, they're just an array of strings.
I've seen other code that seems to require a user conversation sessionId to create contexts. But the intents are global - not for a single conversation. And I assume these are just for tracking context within a single conversation session (or google astronaut engineering)
The data I'm POSTing looks like the below
There's a google example here that doesn't touch contexts
https://cloud.google.com/dialogflow/es/docs/how/manage-intents#create_intent
I've tried contexts as various formats without success
// this is the part that doesn't work
// const contexts = [{
// // name: `${sessionPath}/contexts/${name}`,
// // name: 'test context name'
// }]
const contexts = [
'theater-critics'
]
createIntentRequest {
"parent": "projects/XXXXXXXX-XXXXXXXX/agent",
"intent": {
"displayName": "test 4",
"trainingPhrases": [
{
"type": "EXAMPLE",
"parts": [
{
"text": "this is a test phrase"
}
]
},
{
"type": "EXAMPLE",
"parts": [
{
"text": "this is a another test phrase"
}
]
}
],
"messages": [
{
"text": {
"text": [
"this is a test response"
]
}
}
],
"contexts": [
"theater-critics"
]
}
}
Intent projects/asylum-287516/agent/intents/XXXXXXXX-e852-4c09-bda6-e524b8329db8 created
Full JS (TS) code below for anyone else
import { DfConfig } from './DfConfig'
const dialogflow = require('#google-cloud/dialogflow');
const testData = {
displayName: 'test 4',
trainingPhrasesParts: [
"this is a test phrase",
"this is a another test phrase"
],
messageTexts: [
'this is a test response'
]
}
// const messageTexts = 'Message texts for the agent's response when the intent is detected, e.g. 'Your reservation has been confirmed';
const intentsClient = new dialogflow.IntentsClient();
export const DfCreateIntent = async () => {
const agentPath = intentsClient.agentPath(DfConfig.projectId);
const trainingPhrases = [];
testData.trainingPhrasesParts.forEach(trainingPhrasesPart => {
const part = {
text: trainingPhrasesPart,
};
// Here we create a new training phrase for each provided part.
const trainingPhrase = {
type: 'EXAMPLE',
parts: [part],
};
// #ts-ignore
trainingPhrases.push(trainingPhrase);
});
const messageText = {
text: testData.messageTexts,
};
const message = {
text: messageText,
};
// this is the part that doesn't work
// const contexts = [{
// // name: `${sessionPath}/contexts/${name}`,
// // name: 'test context name'
// }]
const contexts = [
'theater-critics'
]
const intent = {
displayName: testData.displayName,
trainingPhrases: trainingPhrases,
messages: [message],
contexts
};
const createIntentRequest = {
parent: agentPath,
intent: intent,
};
console.log('createIntentRequest', JSON.stringify(createIntentRequest, null, 2))
// Create the intent
const [response] = await intentsClient.createIntent(createIntentRequest);
console.log(`Intent ${response.name} created`);
}
// createIntent();
update figured out based on this
https://cloud.google.com/dialogflow/es/docs/reference/rest/v2/projects.agent.intents#Intent
const contextId = 'runner'
const contextName = `projects/${DfConfig.projectId}/agent/sessions/-/contexts/${contextId}`
const inputContextNames = [
contextName
]
const intent = {
displayName: testData.displayName,
trainingPhrases: trainingPhrases,
messages: [message],
inputContextNames
};
Related
my resolver in schema.py looks like this
def resolve_areas(self, info, **kwargs):
result = []
dupfree = []
user = info.context.user
areas = BoxModel.objects.filter(client=user, active=True).values_list('area_string', flat=True)
In GraphiQL I am using this query:
{
areas {
edges {
node {
id
name
}
}
}
}
And get Output that starts like this:
{
"data": {
"areas": {
"edges": [
{
"node": {
"id": "QXJlYTpkZWZ",
"name": "default"
}
},
{
"node": {
"id": "QXJlYTptZXN",
"name": "messe"
}
},
{
"node": {
"id": "QXJlYTptZXN",
"name": "messe"
}
},
But i want distinct values on the name variable
(Using a MySQL Database so distinct does not work)
SOLVED:
distinct was not working. so i just wrote a short loop which tracked onlye the string names duplicates in a list and only appended the whole "area" object if it's name has not been added to the duplicates list yet
result = []
dupl_counter = []
for area in areas:
if area not in dupl_counter:
dupl_counter.append(area)
result.append(Area(name=area))
print(area)
How do I get a value from nested array of object?
Here is my API looks like:
{
"id": 7,
"code": "ABC123",
"name": "Abu Bakar Enterprise",
"speCompanyDetails": [
{
"id": 1,
"speCompanyId": 7,
"registrationType": "2",
"registrationNo": "12345678",
"registrationYear": 2005,
"annualIncome": 100000,
}
]
}
My objective is I want to get value for code and name but at the same time I also want to fetch value from speCompanyDetails.annualIncome
Currently my query is similar like this:
const SUPPLIER_INFO_QUERY = gql`
query SupplierInfoQuery($mainSuppId: String!) {
supplierInfo(id: $mainSuppId)
#rest(path: "services/supplier/api/spe-companies/{args.id}", method: "GET", type: "SupplierInfo") {
id
name
code
speCompanyDetails
}
}
`;
But the value for anuualIncome is undefined.
const SUPPLIER_INFO_QUERY = gql
query SupplierInfoQuery($mainSuppId: String!) {
supplierInfo(id: $mainSuppId)
#rest(path: "services/supplier/api/spe-companies/{args.id}", method: "GET", type: "SupplierInfo") {
id
name
code
speCompanyDetails{ annualIncome }
}
}
;
I'm playing with the New Data API for Amazon Aurora Serverless
Is it possible to get the table column names in the response?
If for example I run the following query in a user table with the columns id, first_name, last_name, email, phone:
const sqlStatement = `
SELECT *
FROM user
WHERE id = :id
`;
const params = {
secretArn: <mySecretArn>,
resourceArn: <myResourceArn>,
database: <myDatabase>,
sql: sqlStatement,
parameters: [
{
name: "id",
value: {
"stringValue": 1
}
}
]
};
let res = await this.RDS.executeStatement(params)
console.log(res);
I'm getting a response like this one, So I need to guess which column corresponds with each value:
{
"numberOfRecordsUpdated": 0,
"records": [
[
{
"longValue": 1
},
{
"stringValue": "Nicolas"
},
{
"stringValue": "Perez"
},
{
"stringValue": "example#example.com"
},
{
"isNull": true
}
]
]
}
I would like to have a response like this one:
{
id: 1,
first_name: "Nicolas",
last_name: "Perez",
email: "example#example.com",
phone: null
}
update1
I have found an npm module that wrap Aurora Serverless Data API and simplify the development
We decided to take the current approach because we were trying to cut down on the response size and including column information with each record was redundant.
You can explicitly choose to include column metadata in the result. See the parameter: "includeResultMetadata".
https://docs.aws.amazon.com/rdsdataservice/latest/APIReference/API_ExecuteStatement.html#API_ExecuteStatement_RequestSyntax
Agree with the consensus here that there should be an out of the box way to do this from the data service API. Because there is not, here's a JavaScript function that will parse the response.
const parseDataServiceResponse = res => {
let columns = res.columnMetadata.map(c => c.name);
let data = res.records.map(r => {
let obj = {};
r.map((v, i) => {
obj[columns[i]] = Object.values(v)[0]
});
return obj
})
return data
}
I understand the pain but it looks like this is reasonable based on the fact that select statement can join multiple tables and duplicated column names may exist.
Similar to the answer above from #C.Slack but I used a combination of map and reduce to parse response from Aurora Postgres.
// declarative column names in array
const columns = ['a.id', 'u.id', 'u.username', 'g.id', 'g.name'];
// execute sql statement
const params = {
database: AWS_PROVIDER_STAGE,
resourceArn: AWS_DATABASE_CLUSTER,
secretArn: AWS_SECRET_STORE_ARN,
// includeResultMetadata: true,
sql: `
SELECT ${columns.join()} FROM accounts a
FULL OUTER JOIN users u ON u.id = a.user_id
FULL OUTER JOIN groups g ON g.id = a.group_id
WHERE u.username=:username;
`,
parameters: [
{
name: 'username',
value: {
stringValue: 'rick.cha',
},
},
],
};
const rds = new AWS.RDSDataService();
const response = await rds.executeStatement(params).promise();
// parse response into json array
const data = response.records.map((record) => {
return record.reduce((prev, val, index) => {
return { ...prev, [columns[index]]: Object.values(val)[0] };
}, {});
});
Hope this code snippet helps someone.
And here is the response
[
{
'a.id': '8bfc547c-3c42-4203-aa2a-d0ee35996e60',
'u.id': '01129aaf-736a-4e86-93a9-0ab3e08b3d11',
'u.username': 'rick.cha',
'g.id': 'ff6ebd78-a1cf-452c-91e0-ed5d0aaaa624',
'g.name': 'valentree',
},
{
'a.id': '983f2919-1b52-4544-9f58-c3de61925647',
'u.id': '01129aaf-736a-4e86-93a9-0ab3e08b3d11',
'u.username': 'rick.cha',
'g.id': '2f1858b4-1468-447f-ba94-330de76de5d1',
'g.name': 'ensightful',
},
]
Similar to the other answers, but if you are using Python/Boto3:
def parse_data_service_response(res):
columns = [column['name'] for column in res['columnMetadata']]
parsed_records = []
for record in res['records']:
parsed_record = {}
for i, cell in enumerate(record):
key = columns[i]
value = list(cell.values())[0]
parsed_record[key] = value
parsed_records.append(parsed_record)
return parsed_records
I've added to the great answer already provided by C. Slack to deal with AWS handling empty nullable character fields by giving the response { "isNull": true } in the JSON.
Here's my function to handle this by returning an empty string value - this is what I would expect anyway.
const parseRDSdata = (input) => {
let columns = input.columnMetadata.map(c => { return { name: c.name, typeName: c.typeName}; });
let parsedData = input.records.map(row => {
let response = {};
row.map((v, i) => {
//test the typeName in the column metadata, and also the keyName in the values - we need to cater for a return value of { "isNull": true } - pflangan
if ((columns[i].typeName == 'VARCHAR' || columns[i].typeName == 'CHAR') && Object.keys(v)[0] == 'isNull' && Object.values(v)[0] == true)
response[columns[i].name] = '';
else
response[columns[i].name] = Object.values(v)[0];
}
);
return response;
}
);
return parsedData;
}
I have an optional String field, notes, that is sometimes empty. If it's empty I want to insert null, otherwise I want to insert the string.
Here is my resolver -
{
"version" : "2017-02-28",
"operation": "Invoke",
#set($id = $util.autoId())
#set($notes = $util.defaultIfNullOrEmpty($context.arguments.notes, 'null'))
"payload": {
"sql":"INSERT INTO things VALUES ('$id', :NOTES)",
"variableMapping": {
":NOTES" : $notes
},
"responseSQL": "SELECT * FROM things WHERE id = '$id'"
}
}
With this graphql
mutation CreateThing{
createThing() {
id
notes
}
}
I get -
{
"data": {
"createRoll": {
"id": "6af68989-0bdc-44e2-8558-aeb4c8418e93",
"notes": "null"
}
}
}
when I really want null without the quotes.
And with this graphql -
mutation CreateThing{
createThing(notes: "Here are some notes") {
id
notes
}
}
I get -
{
"data": {
"createThing": {
"id": "6af68989-0bdc-44e2-8558-aeb4c8418e93",
"notes": "Here are some notes"
}
}
}
which is what I want.
How do I get a quoteless null and a quoted string into the same field?
TL;DR you should use $util.toJson() to print the $context.arguments.notes correctly. Replace your $notes assignment with
#set($notes = $util.toJson($util.defaultIfNullOrEmpty($context.arguments.notes, null)))
Explanation:
The reason is VTL prints whatever the toString() method returns and your call to
$util.defaultIfNullOrEmpty($context.arguments.notes, 'null') will return the string "null", which will be printed as "null".
If you replace with $util.defaultIfNullOrEmpty($context.arguments.notes, null) then it will return a null string. However, VTL will print $notes because that is the way it handles null references. In order to print null, which is the valid JSON representation of null, we have to serialize it to JSON. So the correct statement is:
#set($notes = $util.toJson($util.defaultIfNullOrEmpty($context.arguments.notes, null)))
Full test:
I'm assuming you started with the RDS sample provided in the AWS AppSync console and modified it. To reproduce, I updated the content field in the Schema to be nullable:
type Mutation {
...
createPost(author: String!, content: String): Post
...
}
type Post {
id: ID!
author: String!
content: String
views: Int
comments: [Comment]
}
and I modified the posts table schema so content can also be null there: (inside the Lambda function)
function conditionallyCreatePostsTable(connection) {
const createTableSQL = `CREATE TABLE IF NOT EXISTS posts (
id VARCHAR(64) NOT NULL,
author VARCHAR(64) NOT NULL,
content VARCHAR(2048),
views INT NOT NULL,
PRIMARY KEY(id))`;
return executeSQL(connection, createTableSQL);
}
This is the request template for the createPost mutation:
{
"version" : "2017-02-28",
"operation": "Invoke",
#set($id = $util.autoId())
"payload": {
"sql":"INSERT INTO posts VALUES ('$id', :AUTHOR, :CONTENT, 1)",
"variableMapping": {
":AUTHOR" : "$context.arguments.author",
":CONTENT" : $util.toJson($util.defaultIfNullOrEmpty($context.arguments.content, null))
},
"responseSQL": "SELECT id, author, content, views FROM posts WHERE id = '$id'"
}
}
and response template:
$util.toJson($context.result[0])
The following query:
mutation CreatePost {
createPost(author: "Me") {
id
author
content
views
}
}
returns:
{
"data": {
"createPost": {
"id": "b42ee08c-956d-4b89-afda-60fe231e86d7",
"author": "Me",
"content": null,
"views": 1
}
}
}
and
mutation CreatePost {
createPost(author: "Me", content: "content") {
id
author
content
views
}
}
returns
{
"data": {
"createPost": {
"id": "c6af0cbf-cf05-4110-8bc2-833bf9fca9f5",
"author": "Me",
"content": "content",
"views": 1
}
}
}
We were looking into the same issue. For some reason, the accepted answer does not work for us. Maybe because it's a beta feature and there is a new resolver version (2018-05-29 vs 2017-02-28, changes here: Resolver Mapping Template Changelog).
We use this for the time being using NULLIF():
{
"version": "2018-05-29",
"statements": [
"INSERT INTO sales_customers_addresses (`id`, `customerid`, `type`, `company`, `country`, `email`) VALUES (NULL, :CUSTOMERID, :TYPE, NULLIF(:COMPANY, ''), NULLIF(:COUNTRY, ''), :EMAIL)"
],
"variableMap": {
":CUSTOMERID": $customerid,
":TYPE": "$type",
":COMPANY": "$util.defaultIfNullOrEmpty($context.args.address.company, '')",
":COUNTRY": "$util.defaultIfNullOrEmpty($context.args.address.country, '')",
":EMAIL": "$context.args.address.email"
}
}
I'm creating a skill in Alexa that does the following.
User : Hi
Alexa, hello please give me your name
User : John
Alexa : Hi John, good to meet you. How old are you
User : 25
Below are my intents
{
"intents": [
{
"intent": "StartTheFlow",
"slots": [
{
"name": "custName",
"type": "list_of_userNames"
},
{
"name": "age",
"type": "AMAZON.NUMBER"
}
]
},
{
"intent": "AMAZON.HelpIntent"
},{
"intent": "Welcome"
},
{
"intent": "AMAZON.StopIntent"
},
{
"intent": "AMAZON.CancelIntent"
}
]
}
And below are my utterances
StartTheFlow Hi
StartTheFlow {custName}
StartTheFlow {age}
Below is my onIntent()
#Override
public SpeechletResponse onIntent(final IntentRequest request, final Session session) throws SpeechletException {
log.info("onIntent requestId={}, sessionId={}", request.getRequestId(), session.getSessionId());
Intent intent = request.getIntent();
String intentName = (intent != null) ? intent.getName() : null;
if ("StartTheFlow".equals(intentName)) {
return getTheFlow(intent, session);
} else if ("AMAZON.HelpIntent".equals(intentName)) {
return getHelpResponse();
} else if ("WelcomeChubb".equals(intentName)) {
return getWelcomeResponse();
} else {
throw new SpeechletException("Invalid Intent");
}
}
And I'm trying to handle this as below
private SpeechletResponse getTheFlow(Intent intent, Session session) {
boolean isAskResponse = true;
String responseText = "";
String nameFromSession = (String) session.getAttribute("name");
if (StringUtils.isNullOrEmpty(nameFromSession)) {
responseText = "please give me your name";
getTheNameText(intent, session);
} else {
System.out.println(session.getAttribute("nameFromSession"));
responseText = "please give me your date of birth";
}
return getSpeechletResponse(responseText, "", isAskResponse);
}
private String getTheNameText(Intent intent, Session session) {
String userNameFrmIntent = getNameFromSlot(intent).toString();
session.setAttribute("nameFromSession", userNameFrmIntent);
return getNameFromSlot(intent).toString();
}
private String getNameFromSlot(Intent intent) {
Slot userName = intent.getSlot(Slot_Name);
return userName.getValue();
}
Also, I've defined a slot in the top as below.
private static final String Slot_Name = "custName";
But here when I type Hi, Instead of asking me my name, it is giving me an error in logs it shows Java NullPointer Exception. the response that I get when I type Hi is as below.
{
"session": {
"sessionId": "SessionId.a2740ca4-73ff-4a15-856d-6461b3c7b2e1",
"application": {
"applicationId": "amzn1.ask.skill.e3dfb30e-0089-423c-a325-30ad28dd2e2b"
},
"attributes": {},
"user": {
"userId": "amzn1.ask.account.AEQYTT5HFHEGGDSUCT3NW45HKR7O3FBL5YCBSZIS7P5LNP5BXFEMUR7AUYOZVKC2FT5V6RKJC7RNA5VMZVREBAXAQP3NFNTQSFSSKSEXIYT4FQYMS5JCI2CCAOPUF4FN4C6DHEU6ONNY3D6GN5AWK75KOQNJH2IWROIIXTPNXSNI6FLQYRBBMP7TRSOWVNCY73WJUT2VLHDACWA"
},
"new": true
},
"request": {
"type": "IntentRequest",
"requestId": "EdwRequestId.cf686fc0-cbfd-4496-bb09-c41714563507",
"locale": "en-US",
"timestamp": "2017-02-15T20:12:44Z",
"intent": {
"name": "StartTheFlow",
"slots": {
"custName": {
"name": "custName",
"value": "hi"
}
}
}
},
"version": "1.0"
}
Can someone please let me know where am I going wrong and how can I fix this, I've quite a number of questions to be linked, like 25, can Someone please let me know if there is a better way to do this in java.
Thanks
I would recommend creating a separate intent for each thing that the user says. So for example, HelloIntent, NameIntent, and AgeIntent.
Then be sure to pass those bits of information forward to all following intents in the session. So each intent could use a common function at the beginning to read each string from the session (if exists), add the new slot data to it, and then write all the strings back to the response session before finishing.
Since you'll then have separate intents, and the user could conceivably say them out of order, you may want to check that all the needed strings have been entered, or else prompt the user for any missing strings.
The problem with saving data in the session is that the data will be gone the next time the user starts the skill. To resolve this, you could use a database to hold the users data, saving it keyed to the userId. There are lots of examples on how to do that. Be careful that some databases are essentially free, but others will charge you depending on how many times it is used each month.