How do I write an item to a DynamoDb with the AWS DynamoDB DocumentClient? - amazon-dynamodb

I'm having trouble with the AWS DynamoDb JS SDK v2.4.9. I want to use the DocumentClient class as opposed to the lower level DynamoDb class, but can't get it working.
This works:
function testPutItem( callback ) {
var tableName = 'todos';
var params = {
TableName: tableName,
Item: {
user_id: { S : userId },
id: { N : msFromEpoch }, // ms from epoch
title: { S : makeRandomStringWithLength(16) },
completed: { BOOL: false }
}
};
var dynamodb = new AWS.DynamoDB();
dynamodb.putItem(params, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else {
console.log(data); // successful response
if (callback) callback(data);
}
});
}
This does not work and gives the error InvalidParameterType: Expected params.Item[attribute] to be a structure for each attribute--as if DocumentClient is expecting the same input as DynamoDb:
function testPutItem( callback ) {
var tableName = 'todos';
var params = {
TableName: tableName,
Item: {
user_id: userId,
id: msFromEpoch,
title: makeRandomStringWithLength(16),
completed: false
}
};
console.log(params);
var docClient = new AWS.DynamoDB.DocumentClient({region: 'us-east-1'});
docClient.put(params, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else {
console.log(data); // successful response
if (callback) callback(data);
}
});
}
Does anyone have any idea what I am doing wrong?

I used to have the same issue,
please try with a simple object first, cause it's due to some special characters in your attributes, see my example :
this generates the error
InvalidParameterType: Expected params.Item[attribute] to be a structure
var Item = {
domain: "knewtone.com",
categorie: "<some HTML Object stuff>",
title: "<some HTML stuff>",
html: "<some HTML stuff>""
};
but when i replace the HTML stuff with a formated Html, simple characters , it works
var Item = {
domain: "knewtone.com",
categorie: $(categorie).html(),
title: $(title).html(),
html: $(html).html()
};

Related

How do I access data in GraphQL if not by the id?

I'm using GraphQL with Meteor and Pup v2, and I have a problem accessing the users data via a special ID provided to every user on signup, this ID will be used in a link (mysite.com/user/specialId) so other users can view the searched users account. Problem is, I can't get the data with the special ID, I can't get any data back if I don't pass in the users _id provided by MongoDB. Below I have a bunch of the code used:
Attempt 1
I tried to use a custom on-the-go way just to be able to at least access the data to see if it works (and then implement it correctly later)
const GET_USER_DETAILS = gql`
query user($userId: String) {
user(userId: $userId) {
userId
username
_id
}
}
`;
Here I export so I can get the data:
export default compose(
graphql(GET_USER_DETAILS, {
options: ({ match }) => ({
fetchPolicy: 'no-cache',
variables: {
// existing specialId for testing purposes, to be replaced with match.params.userId
userId: "J4xZzvvhBDSEufnBn",
},
}),
}),
)(PublicProfileView);
This returns a 400 error Network error: Response not successful: Received status code 400 error and after multiple attempts, I could not fix it, so I tried a different approach...
Attempt 2
I tried to go deep into the files and change the GraphQL. Created a new query:
query userById($userId: String) {
userById(userId: $userId) {
...UserAttributes
}
}
(Mentioned fragment)
fragment UserAttributes on User {
_id
name {
...
}
username
emailAddress
oAuthProvider
roles {
...
}
settings {
...
}
userId
}
Tried to add new item in API:
type Query {
...
userById(userId: String): User
...
}
Resolver:
resolvers: {
Query: {
...
userById: (parent, args) => {
// Assuming args equals an object like { _id: '123' };
return UserQueries.userById(args);
},
},
},
query.js, attempt 1:
userById: (parent) => queryUsers.find({ userId: parent.userId }, { sort: { createdAt: 1 } }).fetch()
Attempt 2:
userById: (parent, args, context) => {
return queryUsers({
userId: parent.userId,
});
},
And finally
Attempt 3
I tried to modify the get query
const getQueryModified = (options) => {
// console.log(options.userId)
try {
return options.userId
? { 'userId': options.userId }
: { userId: options.userId };
} catch (exception) {
throw new Error(`[queryUsers.getQuery] ${exception.message}`);
}
};
Here is the original query I tried to modify:
const getQuery = (options) => {
try {
return options.search
? {
_id: { $ne: options.currentUser._id },
$or: [
{ 'profile.name.first': options.search },
{ 'profile.name.last': options.search },
{ 'emails.address': options.search },
// { 'userId': options.search },
{ 'services.facebook.first_name': options.search },
{ 'services.facebook.last_name': options.search },
{ 'services.facebook.email': options.search },
],
}
: { _id: options.currentUser._id };
} catch (exception) {
throw new Error(`[queryUsers.getQuery] ${exception.message}`);
}
};
Unfortunately this was also unsuccessful, the best I get from these when executing the below query is null...
userById(userId: "J4xZzvvhBDSEufnBn"){
username
}
All I want is to get the user data from their userId and not their _id, but I can't seem to figure out how to do it

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

How do i convert a single json record retrieved from firebase into an array

I am trying to take a single record from firebase to use in vuejs but I cant find out how to convert it to an array, if thats even what i should be doing.
my mutation
GET_CASE(state, caseId) {
state.caseId = caseId;
},
My action
getCase ({ commit, context }, data) {
return axios.get('http' + data + '.json')
.then(res => {
const convertcase = []
convertcase.push({ data: res.data })
//result below of what is returned from the res.data
console.log(convertcase)
// commit('GET_CASE', convertcase)
})
.catch(e => context.error(e));
},
I now get the following returned to {{ myCase }}
[ { "data": { case_name: "Broken laptop", case_status: "live", case_summary: "This is some summary content", contact: "", createdBy: "Paul", createdDate: "2018-06-21T15:20:22.932Z", assessor: "Gould", updates: "" } } ]
when all i want to display is Broken Laptop
Thanks
Example let obj = {a: 1, b: 'a'); let arr = Object.values(obj) // arr = [1, 'a']
async getCase ({ commit, context }, url) {
try {
let { data } = await axios.get(`http${url}.json`)
commit('myMutation', Object.values(data))
} catch (error) {
context.error(error)
}
}
But as I'm reading your post again, I think you don't want array from object. You want array with one object. So, maybe this is what you want:
async getCase ({ commit, context }, url) {
try {
let { data } = await axios.get(`http${url}.json`)
commit('myMutation', [data])
} catch (error) {
context.error(error)
}
}
Put this inside / after your .then
Object.keys(data).forEach(function(k, i) {
console.log(k, i);
});
With a response from Axios, you can get your data as:
res.data.case_name
res.data.case_number
....
Just build JavaScript object holding these properties and pass this object to your mutation. I think it is better than using an array.
const obj = {};
Object.assign(obj, res.data);
commit('GET_CASE', obj)
And in your mutation you do as follows:
mutations: {
GET_CASE (state, payload) {
for (var k in payload) {
if (payload.hasOwnProperty(k)) {
state[k] = payload[k]
}
}
}
}
Alternatively you can code your store as follows:
state: {
case: {},
...
},
getters: {
getCase: state => {
return state.case
},
....
},
mutations: {
GET_CASE (state, payload) {
state.case = payload
}
}
and you call the value of a case field form a component as follows:
const case = this.$store.getters.getCase
..... = case.case_name

Meteor observe not working properly in 1.6

I recently upgraded from 1.2 to Meteors latest version 1.6.0.1.
I was using observe in a publication and an observe on the client to get changes.
in 1.2 no problems at all, but in 1.6 observed changes are not received in a "changed" client callback, but the client does get the ddp message. I can verify that by looking in Chromes dev tools > websocket, see the incoming message, but it's never fired in a client callback. This only happens when changing 2-3 documents at a time.
So when I delete a few documents from the DB, the publication fires off the callbacks, and the client receives them in the websocket messages, but it only fires once in the "observe" callback on the client.
Here is my code.
Client -
CollectionTest = new Meteor.Collection('collectionTest');
CollectionTest.find({}).observe({
added: function (doc) {
console.log("ADDED DOC ", doc);
},
changed: function (newDoc, oldDoc) {
console.log("CHANGED DOC new ", newDoc);
},
removed: function (doc) {
console.log("REMOVED DOC ", doc);
}
});
Server Publication -
Meteor.publish("ddpPub", function () {
var self = this,
ready = false;
var userId = self.userId;
var subHandle = TestData.find({}).observeChanges({
added: function (id, fields) {
if (ready) {
self.changed("collectionTest", userId, {
type: "added",
data: {
id: id,
fields: fields
}
});
}
},
changed: function (id, fields) {
if (ready) {
self.changed("collectionTest", userId, {
type: "changed",
data: {
id: id,
fields: fields
}
});
}
},
removed: function (id) {
if (ready) {
self.changed("collectionTest", userId, {
type: "removed",
data: id
});
}
}
});
self.added("collectionTest", userId);
self.ready();
ready = true;
self.onStop(function () {
subHandle.stop();
});
});
Attached are images from me removing the documents from the DB. The websocket messages, and then my console on the client. Showing it only fires once for 5 documents.
Showing the document id's I am deleting
DDP messages in 'websocket' confirmed they get to client
Single client message in client callback showing only document changed
UPDATE: 12/15/17 - 7:17pm PST
After working on this for a couple hours, finding some related meteor posts with observe callbacks and “Meteor.call” not working inside, the solution or hack is to wrap the “Meteor.call” in a “setTimeout” with the value of 0, and it fixes it.
I tried that here, and it didn’t work, but then I tried throttle the response, and it works! Not sure if it's a reliable fix, but it's the only one I found so far.
I am not sure why this works, or what causes the problem in the first place, any explanation would be welcome.
Server Publication -
Meteor.publish("ddpPub", function () {
var self = this,
ready = false;
var userId = self.userId;
var subHandle = TestData.find({}).observeChanges({
added: function (id, fields) {
if (ready) {
console.log("ADDING PUBLICATION");
self.changed("collectionTest", userId, {
type: "added",
data: {
id: id,
fields: fields
}
});
}
},
changed: function (id, fields) {
if (ready) {
console.log("CHANGING PUBLICATION");
self.changed("collectionTest", userId, {
type: "changed",
data: {
id: id,
fields: fields
}
});
}
},
removed: function (id) {
if (ready) {
console.log("REMOVING PUBLICATION");
ratePub(id, function (data) {
console.log("OBJECT DATA IS ", data);
self.changed("collectionTest", userId, data);
});
}
}
});
self.added("collectionTest", userId);
self.ready();
ready = true;
self.onStop(function () {
subHandle.stop();
});
});
var returnPub = function (id, callback) {
console.log("RETURNING PUB ");
callback({
id: id,
type: "removed",
data: id
});
};
var ratePub = _.rateLimit(returnPub, 10);

how to insert data into SQLite using ionic

I am new to ionic.I want to add data into SQLite which is coming from remote server. I have successfully populated data into list.so how can i store this data into sqlite. here is my code. how do i pass this data to query.I am unable to do this.
service.js
angular.module('starter.service',[]).
factory('userServices',['$http',function($http){
var users = [];
return {
get: function(){
return $http.get("http://xxxxxxxxx-info").then(function(response){
users = response.data;
return users;
});
},
remove:function(content){
users.splice(users.indexOf(content),1);
},
getUser:function(chatId)
{
for(var i=0; i<users.length;i++){
if(users[i].content_id === parseInt(chatId)){
return users[i];
}
}
return null;
}
}
}]);
controller.js
angular.module('shoppingPad.controller', [])
.controller('ChatCtrl', function ($scope, userServices, $ionicModal, $cordovaSQLite) {
console.log('inside controller');
userServices.get().then(function (users) {
//users is an array of user objects
$scope.contents = users;
console.log($scope.contents);
var query = "INSERT INTO content (content_id, display_name) VALUES (?,?)";
$cordovaSQLite.execute(db, query, [users.content_id, users.display_name]).then(function (res) {
alert(res);
alert('Inserted');
}, function (e) {
alert('Error:' + e.message);
});
});
Where did you define db? It's necessary to wait until device is ready.
$ionicPlatform.ready(function () {
var db = $cordovaSQLite.openDB({ name: "my.db" });
// just first time you need to define content table
$cordovaSQLite.execute(db,"CREATE TABLE content (content_id integer, display_name text)");
userServices.get().then(function (users) {
//users is an array of user objects
$scope.contents = users;
console.log($scope.contents);
var query = "INSERT INTO content (content_id, display_name) VALUES (?,?)";
$cordovaSQLite.execute(db, query, [users.content_id, users.display_name]).then(function (res) {
alert(res);
alert('Inserted');
}, function (e) {
alert('Error:' + e.message);
});
});
});
Are you sure, that your object users look like
{
"content_id":12,
"display_name":"hello world"
}
and not like
[
{
"content_id":12,
"display_name":"hello world"
},
{
"content_id":13,
"display_name":"stackoverflow"
},
...
]
I just ask, because users sounds like more than one entry.

Resources