alexa sdk: can't get persitentAttributes - alexa-skills-kit

i'm trying to add persistent attributes to my lambda function.
i created a dynamoDB table and added it to the triggers of my lambda function.
i copied a sample code from github, but when i try to launch the skill i get an error. The console log shows:
{
"errorMessage": "Could not read item (amzn1.ask.account.AGIIYNRXWDLBD6XEPW72QS2BHGXNP7NWYBEWSH2XLSXZP64X3NCYEMVK233VFDWH77ZB6DAK6YJ53SZLNUFVQ56CYOVCILS7QFZI4CIRDWC3PAHS4QG27YUY5PTT6QEIK46YFNTJT54YAKNGOWV2UO66XZACFDQ5SEXKJYOBNFNIZNUXKNTIAAYZG4R5ZU4FMLPDZZN64KLINNA) from table (Spiele): The provided key element does not match the schema",
"errorType": "AskSdk.DynamoDbPersistenceAdapter Error",
"stackTrace": [
"Object.createAskSdkError (/var/task/node_modules/ask-sdk-dynamodb-persistence-adapter/lib/utils/AskSdkUtils.js:22:17)",
"DynamoDbPersistenceAdapter.<anonymous> (/var/task/node_modules/ask-sdk-dynamodb-persistence-adapter/lib/attributes/persistence/DynamoDbPersistenceAdapter.js:123:49)",
"step (/var/task/node_modules/ask-sdk-dynamodb-persistence-adapter/lib/attributes/persistence/DynamoDbPersistenceAdapter.js:44:23)",
"Object.throw (/var/task/node_modules/ask-sdk-dynamodb-persistence-adapter/lib/attributes/persistence/DynamoDbPersistenceAdapter.js:25:53)",
"rejected (/var/task/node_modules/ask-sdk-dynamodb-persistence-adapter/lib/attributes/persistence/DynamoDbPersistenceAdapter.js:17:65)",
"<anonymous>",
"process._tickDomainCallback (internal/process/next_tick.js:228:7)"
]
}
the table contains a primary key "name" and sort key "UserId". is that wrong?
here is my index.js:
const Alexa = require('ask-sdk');
// Define the skill features
let skill;
/**
* If this is the first start of the skill, grab the user's data from Dynamo and
* set the session attributes to the persistent data.
*/
const GetUserDataInterceptor = {
process(handlerInput) {
let attributes = handlerInput.attributesManager.getSessionAttributes();
if (handlerInput.requestEnvelope.request.type === 'LaunchRequest' && !attributes['isInitialized']) {
return new Promise((resolve, reject) => {
handlerInput.attributesManager.getPersistentAttributes()
.then((attributes) => {
attributes['isInitialized'] = true;
saveUser(handlerInput, attributes, 'session');
resolve();
})
.catch((error) => {
reject(error);
})
});
}
}
};
function saveUser(handlerInput, attributes, mode) {
if(mode === 'session'){
handlerInput.attributesManager.setSessionAttributes(attributes);
} else if(mode === 'persistent') {
console.info("Saving to Dynamo: ",attributes);
return new Promise((resolve, reject) => {
handlerInput.attributesManager.getPersistentAttributes()
.then((persistent) => {
delete attributes['isInitialized'];
handlerInput.attributesManager.setPersistentAttributes(attributes);
resolve(handlerInput.attributesManager.savePersistentAttributes());
})
.catch((error) => {
reject(error);
});
});
}
}
const LaunchHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'LaunchRequest';
},
handle(handlerInput) {
console.info("LaunchRequest");
let attributes = handlerInput.attributesManager.getSessionAttributes();
console.info("Test the load: " + attributes['isInitialized']);
attributes['FOO'] = "BAR";
saveUser(handlerInput, attributes, 'persistent');
return handlerInput.responseBuilder
.speak('Hello')
.reprompt('Hello')
.getResponse();
}
}
exports.handler = Alexa.SkillBuilders.standard()
.addRequestHandlers(
LaunchHandler
)
.addRequestInterceptors(GetUserDataInterceptor)
.withTableName('Spiele')
.withAutoCreateTable(true)
.withDynamoDbClient()
.lambda();
can anyone tell me what i'm doing wrong?

please confirm the partition key is 'userId' not 'UserId' (notice the uppercase U).
Also I would suggest using 'this' object.
Let me know if that helps.
Cheers

Below code is for python lambda function
from ask_sdk_core.skill_builder import CustomSkillBuilder
from ask_sdk_dynamodb.adapter import DynamoDbAdapter
sb = SkillBuilder()
sb = CustomSkillBuilder(persistence_adapter = dynamodb_adapter)

Related

$match aggregate in mongoose

I am using $match to find a userid that is reference in my schema. When I pass in http://localhost:3001/api/orders/5d6f1611d3bcd442bc273211/2018-02-01/2019-09-01 I get the following:
5d6f1611d3bcd442bc273211
[0] 2018-02-01
[0] 2019-09-01
[0] ReferenceError: $match is not defined
Here is my code:
getOrderTotal: function (req, res) {
const userID = req.params.userid;
const firstDate = req.params.firstdate;
const secondDate = req.params.seconddate;
console.log(userID);
console.log(firstDate);
console.log(secondDate);
Order
.aggregate([{ $match: { 'user': userID } }])
.find({ "created_at": { "$gte": firstDate, "$lt": secondDate } })
.then(dbModel => res.json(dbModel))
.catch(err => res.status(422).json(err));
}
When I remove the line containing $match, I get the expected results. When I leave it in, I get an empty array.
convert userId to ObjectID
var mongoose = require('mongoose');
var id = mongoose.Types.ObjectId(userID);
Then use id in your $match aggregation
or
.aggregate([{ $match: { 'user': mongoose.Types.ObjectId(userID) } }])
You can use $find and $match at one time using expr.
const userID = req.params.userid;
const firstDate = req.params.firstdate;
const secondDate = req.params.seconddate;
console.log(userID);
console.log(firstDate);
console.log(secondDate);
Order.find({
"$expr": {
"$and": [
{ "$eq": ["$user", userID] },
{ "$gte": [{ "$created_at": firstDate }]},
{ "$lt": [{ "$created_at": secondDate }]}
]
}
}).exec((err , found)=>{
});
I am guessing this is what you want to achieve
Find document that has user matching userId
and has date greater than or equal to firstDate and less than secondDate
If you insist on using the aggregate pipeline plus $match operator, you must then be aware of the following
Note:
The documents returned are plain javascript objects, not mongoose documents (since any shape of document can be returned). More info here: https://mongoosejs.com/docs/api/aggregate.html
Example:
getOrderByDate: async (req, res) => {
// Get the parameter data from req.params
let { user, firstDate, secondDate } = req.params;
// Convert the user data type to mongoose ObjectId
user = mongoose.Types.ObjectId(user);
// Convert both firstDate and SecondDate to date
firstDate = new Date(firstDate);
firstDate.setHours(0, 0, 0, 0);
secondDate = new Date(secondDate);
secondDate.setHours(23, 59, 59, 999);
// See the output in your console if you will...
console.log({ user, firstDate, secondDate });
// Using async (you can still use promises if you will)
try {
const matchedDocuments = await OrderDocument.aggregate([{ $match: { user: user } }]);
// Filter returned document
const result = matchedDocuments.filter((docs) => {
if (docs.created_at >= firstDate && docs.created_at < secondDate) {
return true;
}
});
res.status(200).json({ result });
} catch (error) {
res.status(500).json({ error, message: 'Something went wrong' });
}
}

How to dynamically update an attribute in a dynamodb item?

I created an item in dynamodb using Node js, the item has multiple attributes such as brand, category, discount, validity, etc. I am using uuid to generate ids for each item. Now let's say I want to update the validity attribute of the item, in which case I am currently sending the entire json object with the value of validity modified to the new value.
This is definitely not optimal, please help me find an optimal solution.
const params = {
TableName: process.env.PRODUCT_TABLE,
Key: {
id: event.pathParameters.id,
},
ExpressionAttributeNames: {
'#discount': 'discount',
},
ExpressionAttributeValues: {
':brand': data.brand,
':category': data.category,
':discount': data.discount,
':denominations': data.denominations,
":validity": data.validity,
":redemption": data.redemption
},
UpdateExpression: 'SET #discount = :discount, denominations = :denominations, brand = :brand, category = :category, validity = :validity, redemption = :redemption',
ReturnValues: 'ALL_NEW',
};
I want to send just the attribute I want to update with the new value, if I want to change the validity from 6 months to 8 months, I should just send something like:
{
"validity": "8 months"
}
And it should update the validity attribute of the item.
Same should apply to any other attribute of the item.
'use strict';
const AWS = require('aws-sdk');
const dynamoDb = new AWS.DynamoDB.DocumentClient();
module.exports.update = (event, context, callback) => {
const data = JSON.parse(event.body);
let attr = {};
let nameobj = {};
let exp = 'SET #';
let arr = Object.keys(data);
let attrname = {};
arr.map((key) => {attr[`:${key}`]=data[key]});
arr.map((key) => {
exp += `${key} = :${key}, `
});
arr.map((key) => {nameobj[`#${key}`]=data[key]});
attrname = {
[Object.keys(nameobj)[0]] : nameobj[Object.keys(nameobj)[0]]
}
const params = {
TableName: process.env.PRODUCT_TABLE,
Key: {
id: event.pathParameters.id,
},
ExpressionAttributeNames: attrname,
ExpressionAttributeValues: attr,
UpdateExpression: exp,
ReturnValues: 'ALL_NEW',
};
// update the todo in the database
dynamoDb.update(params, (error, result) => {
// handle potential errors
if (error) {
console.error(error);
callback(null, {
statusCode: error.statusCode || 501,
headers: { 'Content-Type': 'text/plain' },
body: 'Couldn\'t update the card',
});
return;
}
// create a response
const response = {
statusCode: 200,
body: JSON.stringify(result.Attributes),
};
callback(null, response);
});
};
Contrary to others comments, this is very possible, use the UpdateItem action.
Language agnostic API docs
JavaScript specific API docs
If you want to dynamically create the query, try something like this:
const generateUpdateQuery = (fields) => {
let exp = {
UpdateExpression: 'set',
ExpressionAttributeNames: {},
ExpressionAttributeValues: {}
}
Object.entries(fields).forEach(([key, item]) => {
exp.UpdateExpression += ` #${key} = :${key},`;
exp.ExpressionAttributeNames[`#${key}`] = key;
exp.ExpressionAttributeValues[`:${key}`] = item
})
exp.UpdateExpression = exp.UpdateExpression.slice(0, -1);
return exp
}
let data = {
'field' : { 'subfield': 123 },
'other': '456'
}
let expression = generateUpdateQuery(data)
let params = {
// Key, Table, etc..
...expression
}
console.log(params)
Output:
{
UpdateExpression: 'set #field = :field, #other = :other',
ExpressionAttributeNames: {
'#field': 'field',
'#other': 'other'
},
ExpressionAttributeValues: {
':field': {
'subfield': 123
},
':other': '456'
}
}
Using Javascript SDK V3:
Import from the right package:
import { DynamoDBClient PutItemCommandInput, UpdateItemCommandInput, UpdateItemCommand } from '#aws-sdk/client-dynamodb';
Function to dynamically do partial updates to the item:
(the code below is typescript can be easily converted to Javascript, just remove the types!)
function updateItem(id: string, item: any) {
const dbClient = new DynamoDBClient({region: 'your-region-here });
let exp = 'set ';
let attNames: any = { };
let attVal: any = { };
for(const attribute in item) {
const valKey = `:${attribute}`;
attNames[`#${attribute}`] = attribute;
exp += `#${attribute} = ${valKey}, `;
const val = item[attribute];
attVal[valKey] = { [getDynamoType(val)]: val };
}
exp = exp.substring(0, exp.length - 2);
const params: UpdateItemCommandInput = {
TableName: 'your-table-name-here',
Key: { id: { S: id } },
UpdateExpression: exp,
ExpressionAttributeValues: attVal,
ExpressionAttributeNames: attNames,
ReturnValues: 'ALL_NEW',
};
try {
console.debug('writing to db: ', params);
const command = new UpdateItemCommand(params);
const res = await dbClient.send(command);
console.debug('db res: ', res);
return true;
} catch (err) {
console.error('error writing to dynamoDB: ', err);
return false;
}
}
And to use it (we can do partial updates as well):
updateItem('some-unique-id', { name: 'some-attributes' });
What i did is create a helper class.
Here is a simple function : Add all the attribute and values that goes into, if the value is null or undefined it won't be in the expression.
I recommande to create a helper class with typescript and add more functions and other stuff like generator of expressionAttributeValues , expressionAttributeNames ... , Hope this help.
function updateExpression(attributes, values) {
const expression = attributes.reduce((res, attribute, index) => {
if (values[index]) {
res += ` #${attribute}=:${attribute},`;
}
return res;
}, "SET ");
return expression.slice(0, expression.length - 1)
}
console.log(
updateExpression(["id", "age", "power"], ["e8a8da9a-fab0-55ba-bae3-6392e1ebf624", 28, undefined])
);
You can use code and generate the params object based on the object you provide. It's just a JavaScript object, you walk through the items so that the update expression only contains the fields you have provided.
This is not really a DynamoDB question in that this is more a general JS coding question.
You can use UpdateItem; to familiarize yourself with DynamoDb queries I would suggest you DynamoDb NoSQL workbench:
https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/workbench.settingup.html
It can generate snippets for you based on your queries.
DynamoDb NoSQL workbench screenshot query

firebase reset password controller

Yesterday my app was launched, Ionic v1, and a few users entered the wrong password and can't log into the app.
The app uses firebase authentication. I have a __refs file that points to the database and have tried numerous things trying to get the reset to work.
I've tried referencing $firebaseAuth, of course my __refs, $firebase then use $firebase.auth()...
I didn't write the authentication of this app so I'm not real sure how it works. I'm hoping that someone can help me.
My reset controller
angular.module('formulaWizard').controller('ResetPasswordCtrl',
function($scope, $ionicLoading, $firebaseAuth, __Refs) {
$scope.user = {
email: ''
};
$scope.errorMessage = null;
var fbAuth = $firebaseAuth(__Refs.rootRef);
$scope.resetPassword = function() {
$scope.errorMessage = null;
$ionicLoading.show({
template: 'Please wait...'
});
fbAuth.sendPasswordResetEmail($scope.user.email)
.then(showConfirmation)
.catch(handleError);
};
function showConfirmation() {
$scope.emailSent = true;
$ionicLoading.hide();
}
function handleError(error) {
switch (error.code) {
case 'INVALID_EMAIL':
case 'INVALID_USER':
$scope.errorMessage = 'Invalid email';
break;
default:
$scope.errorMessage = 'Error: [' + error.code + ']';
}
$ionicLoading.hide();
}
});
My Refs file
angular.module('formulaWizard')
.factory('__Refs', function ($firebaseArray, $firebaseObject) {
// Might use a resource here that returns a JSON arrayf
var ref = new Firebase('https://firebasedatabase.com/');
return {
rootRef: ref,
customers: ref.child('customers'),
}
});
I can't take credit for the answer it was provide by Abimbola Idowu on HackHands.
Since I paid for the answer I thought I would share it with anyone else that might also be stumped by this.
$scope.resetPassword = function() {
$scope.errorMessage = null;
$ionicLoading.show({
template: 'Please wait...'
});
__Refs.rootRef.resetPassword({ email: $scope.user.email }, function(error) {
if (error === null) {
showConfirmation();
} else {
handleError()
}
});
};
This is the __refs service
angular.module('formulaWizard')
.factory('__Refs', function ($firebaseArray, $firebaseObject) {
// Might use a resource here that returns a JSON arrayf
var ref = new Firebase('https://firebasedatabase.com/');
return {
rootRef: ref,
}
});

Unable to map autocomplete valueChanges with Cloud Firestore database

I am using material autocomplete in a project but unlike their example, I'm pulling from cloud firestore database (beta).
I previously set up a service to handle retrieving the data from json server and this worked fine.
ngOnInit() {
this.branches = this.branchService.get_branches();
...
}
Since moving to the firebase I'm successfully able to display my data but typeahead functionality is not working as expected.
I've tried using both valueChanges() and snapshotChanges() but neither appears to make a difference and I don't know why.
component.ts file:
branches: any = [];
branchCtrl: FormControl = new FormControl();
filteredBranches: Observable<any[]>;
branchCol: AngularFirestoreCollection<Branch>;
branch$: Observable<Branch[]>;
constructor( private afs: AngularFirestore ) {
}
ngOnInit() {
this.branchCol = this.afs.collection('branches');
//this.branch$ = this.branchCol.valueChanges();
this.branch$ = this.branchCol.snapshotChanges().map(actions => { return actions.map(action => { const data = action.payload.doc.data() as Branch; const id = action.payload.doc.id; return { id, ...data }; }); });
this.branches = this.branch$;
this.filteredBranches = this.branchCtrl.valueChanges
.startWith(null)
//.map(b => b && typeof b === 'object' ? b.name : b)
.switchMap(val => {
return this.filterBranches(val || '')
});
}
displayFn(b): string {
return b ? b.name : b;
}
filterBranches(val: string) {
return this.branches
.map(response => response.filter(option => {
return option.name.toLowerCase().indexOf(val.toLowerCase()) === 0
}));
}
getBranch(value){
console.log('branch selected');
}
My assumption is filteredBranches is not able to map the value changes correctly due to collection/document data structure of firebase. Any help would greatly be appreciated.

extending firebase.database.Reference

in the old FB I added a helper function to get/set values as follows:
// val() -> get(), resolve with value at ref
// val(value) -> set(value), resolve with value
// val(vals) -> update(vals), resolve with vals
Firebase.prototype.val = function(vals) {
let self=this;
if (!vals) {
return this.once('value').then(
snapshot => {
if (typeof snapshot.val() === 'undefined' || snapshot.val() === null) throw 'INVALID_VALUE';
return snapshot.val();
},
err => {
throw err;
});
}
let singleVal=(vals.constructor != Object); // is singleVal then vals is a single value
if (singleVal ) return this.set(vals); // set single value
if (!singleVal) return this.update(vals).then(() => vals); // update multiple values
};
}
I could then do for example return ref.child(...).val();
This function does not run in V3.
How can I extend firebase in that way in V3 ?
thx!
Here's the solution - I find this extension very handy
// val() -> get(), resolve with value at ref, fails with error.code
// val(value) -> set(value), resolve with value, fails with error.code
// val(vals) -> update(vals), resolve with vals, fails with error.code
firebase.database.Reference.prototype.val = function(vals) {
let path=this.toString().substring(firebase.database().ref().toString().length-1);
let valsAsString = (typeof vals==='string' ? vals : JSON.stringify(vals));
if (!vals) {
return this.once('value').then(
snapshot => {
if (typeof snapshot.val() === 'undefined' || snapshot.val() === null) {
console.log('val('+path+') failed (null) ! '+error.message+' ('+error.code+')');
throw 'INVALID_VALUE';
}
return snapshot.val(); },
error => {
console.log('val('+path+') failed ! '+error.message+' ('+error.code+')');
throw error.code;
});
}
let singleVal=(vals.constructor != Object); // is singleVal then vals is a single value
if (singleVal ) return this.set(vals).then( // set single value
() => {
return vals;
}, error => {
console.log('val('+path+','+valsAsString+') failed ! '+error.message+' ('+error.code+')');
throw error.code;
}
);
return this.update(vals).then( // update multiple values
() => {
return vals;
}, error => {
console.log('val('+path+','+valsAsString+') failed ! '+error.message+' ('+error.code+')');
throw error.code;
}
);
};
}

Resources