JS - atob() The string to be decoded contains invalid characters - javascript

I'm having some string conversion issues in JS. I have a json object that I want to base64 encode and store as a client side cookie. It seems simple enough but for some reason the JS atob is just not working for me. I keep getting this error
InvalidCharacterError: The string to be decoded contains invalid characters.
Here is a simplified version of why I'm trying to accomplish:
function setCookie(name, value, days) {
var d = new Date;
d.setTime(d.getTime() + 24*60*60*1000*days);
document.cookie = name + "=" + value + ";path=/;expires=" + d.toGMTString();
}
function getCookie(name) {
var v = document.cookie.match('(^|;) ?' + name + '=([^;]*)(;|$)');
return v ? v[2] : null;
}
function getUser() {
let user = getCookie('ds_user')
if (!user) {
return null
}
return JSON.parse(atob(user))
}
const mockUser = {
user: {
id: "1671",
email: "artvandalay#industries.com",
username: "art",
firstName: "Art",
lastName: "Vandalay",
phone: null,
admin: true,
title: "",
guid: "u0000ZDCF4",
vendorUser: false,
lastLogin: "2019-06-07 18:52:11",
defaultStoreId: "6",
},
store: {
storeId: 6,
name: "Demo Store",
marketId: 13
}
}
setCookie('ds_user', JSON.stringify(btoa(mockUser)), 7)
console.log(getUser())
My fiddle: https://jsfiddle.net/u1zjsqyn/
I have tried following other solutions from similar posts like https://stackoverflow.com/a/9786592/5025769 , but no luck

mockUser is an object, when you do btoa(mockUser) you end up with [Object, object], the string version of any object, as btoa can't parse objects.
You want to stringify the object before converting to Base64, then when you get the data back, you do what you're doing, decode Base64 first, then parse as object.
setCookie('ds_user', btoa(JSON.stringify(mockUser)), 7)
FIDDLE

Related

Mongoose: updating new values only if not null

I already got another solution but cannot understand why mine doesn't work.
I tried to use $set: here but it didn't help. objForUpdate do return "name, lastname" when i print. If i replace {objForUpdate} with {name, lastname} - update works. But I cannot pass the parameters in variable.
//Route to UPDATE user
async updateUser(req, res) {
let { _id, type, name, lastname } = req.body;
try {
let objForUpdate = "";
for (var key in req.body) {
if (req.body.hasOwnProperty(key) && req.body.key !== null && req.body[key] !== "" && key !== '_id') {
console.log("this is key: " + key + ", and this is value req.body[key]: " + req.body[key]);
objForUpdate += key + ", ";
}
}
objForUpdate = objForUpdate.slice(0, -2);
const updated = await Users.updateOne({ _id }, {objForUpdate});
res.send({ updated });
} catch (error) {
res.send({ error });
}
}
If you would debug the code, you'll get it.
Lets say the req.body looks like { name: "foo", lastname: "bar" type: "2", _id: 1} and by the end of the for-loop and the slice op the objForUpdate would be "name, lastname, type" which is a string.
Now when you pass this string to the updateOne op, this part {objForUpdate} will get converted to { objForUpdate: "name, lastname, type" } (a conversion of identifier as key and its defined value as the value of that key).
And that update object is incorrect even with $set operator.
If i replace {objForUpdate} with {name, lastname} - update works. Why?
Because in this case with Object destructuring you unpacked the object as independent variables (name, lastname ...) with values. In this case the {objForUpdate} when passed would become {"name":"foo", "lastname":"bar", "type":"2"} which is correct as update object.

AWS DynamoDB Scan Operation Works w/ Class Level Scan, but not Document Writer Scan

The following params return the expected result in a scan operation using dynamoDB.scan(params, function);
const dynamoDB = new AWS::DynamoDB();
let params = {
TableName: ACCOUNT_USAGE_TABLE,
ExpressionAttributeValues: {
":start_date": {
S: new Date(event.start_date).toISOString(),
},
":end_date": {
S: new Date(event.end_date).toISOString()
}
},
ExpressionAttributeNames: {
"#usage_date": "date",
},
FilterExpression: "#usage_date BETWEEN :start_date AND :end_date",
ExclusiveStartKey: event.LastEvaluatedKey
};
But the preceding params fail with when using the dynamoDB.documentClient.scan(params, function) with,
ValidationException: Invalid FilterExpression: Incorrect operand type for operator or function; operator or function: BETWEEN, operand type: M
Any ideas as to why?
Of course, the answer was staring me directly in the face.
In case anybody else runs into it and doesn't see it immediately.
The DynamoDB Document Client in javascript uses javascript types and therefore saw the start_date and end_date objects ([object]) a.k.a operand type: M.
All I had to do is change the ExpressionAttributeValues to
ExpressionAttributeValues: {
":start_date": new Date(event.start_date).toISOString(),
":end_date": new Date(event.end_date).toISOString()
},
And voilĂ , it works.
This code below worked for my scenario, to query between two dates. The error I got is "Invalid FilterExpression: Incorrect operand type for operator or function; operator or function: BETWEEN, operand type: M".
let params = {
TableName: tableName,
ProjectionExpression:"Id, FirstName, LastName, #ProcessedFlag, #CreatedTimeStamp",
FilterExpression: '#CreatedTimeStamp BETWEEN :fromDateTime AND :toDateTime AND #ProcessedFlag = :flag',
ExpressionAttributeNames: {
'#ProcessedFlag': processedFlagName,
'#CreatedTimeStamp': createdTimeStampName,
},
ExpressionAttributeValues: {
':flag' : flag,
':fromDateTime' : new Date(fromDateTime).toISOString() ,
':toDateTime' : new Date(toDateTime).toISOString(),
}
};

Use fetch to send get request with data object

I'm using Fetch (Fetch API) in a project and I would like to, for consistence purposes, create a function that receives all the parameters such as method, url and data and creates the correct request, depending if it's a GET or a POST request.
Is it possible, using Fetch, to send a data object that for the GET request, converts data into and string with the parameters and if it is a POST request, it just sends the data object in the body?
It would look like this:
fetch ('/test', {
method: 'GET',
data: {
test: 'test'
}
});
This doubt was inspired by this jQuery ajax behaviour:
$.ajax({
url: '/test',
method: 'GET',
data: {
test: 'test'
}
});
This would produce this request:
'/test/?test=test'
If I pass the data object as normal in the fetch constructor for a
GET request, would it send the request like the example I gave
'/test/?test=test'
If you want to add query string to a fetch request :
From the SPEC
var url = new URL("https://a.com/method"),
params = {a:1, b:2}
Object.keys(params).forEach(key => url.searchParams.append(key, params[key]))
fetch(url)
this will produce a request :
you could either use the Url class:
var url = new URL("/test/")
Object.keys({test: 'test', a: 1}).forEach(key => url.searchParams.append(key, params[key]))
fetch(url);
or parse the string yourself, if you want wider browser support:
var params = {test: 'test', a: 1},
qs = Object.keys(params).reduce(function(_qs, k, i){ return _qs + '&' + k + '=' + params[k]; }, '').substring(1);
console.log(qs)
I'll show you snippets for creating query with and without using URLSearchParams.
The code will be in typescript for the if you're using it. if not, just remove types, it will work in the same way.
Simple solution (URLSearchParams)
/**
* Creates query from given object
* - It doesn't work with deep nesting
* - It doesn't remove empty fields
* #returns `state1=6&state2=horse` without `?`
*/
function createQuery(queryObject?: Record<string | number, unknown> | null): string {
if (queryObject == null) return ""
// Typescript: The `as ...` expression here is ok because `URLSearchParams` will convert non-string by itself
const searchParams = new URLSearchParams(queryObject as Record<string, string>)
return searchParams.toString()
}
Solving problems solution (URLSearchParams)
/**
* Creates query from given object
* - It doesn't work with deep nesting
* - Removes empty fields
* #returns `state1=6&state2=horse` without `?`
*/
function createQuery(queryObject?: Record<string | number, unknown> | null): string {
if (queryObject == null || !Object.keys(queryObject).length) return ""
for (const key in queryObject) {
if (Object.prototype.hasOwnProperty.call(queryObject, key)) {
const value = queryObject[key]
// Use `!value` expression if you want to delete values as `0` (zero) and `""` (empty string) too.
if (value == null) delete queryObject[key]
}
}
const searchParams = new URLSearchParams(queryObject as Record<string, string>)
return searchParams.toString()
}
No URLSearchParams solution
/**
* Creates query from given object
* - Supports prefixes
* - Supports deep nesting
* - Removes empty fields
* #returns `state1=6&state2=horse` without `?`
*/
function createQuery(queryObject?: Record<string | number, unknown> | null, keyPrefix?: string): string {
if (queryObject == null || !Object.keys(queryObject).length) return ""
keyPrefix = keyPrefix ? (keyPrefix + "_") : ""
const queryKeys = Object.keys(queryObject)
const queryArray = queryKeys.map(key => {
const value = queryObject[key]
if (value) {
if (isDictionary(value)) {
return createQuery(value, keyPrefix + key + "_")
}
return keyPrefix + encodeURIComponent(key) + "=" + encodeURIComponent(String(value))
}
return ""
})
return queryArray.filter(Boolean).join("&")
}
isDictionary Helper
I used isDictionary helper here too, you can find it here
Usage
You need to put ? in the beginning of your endpoint plus createQuery
fetch("/test?" + createQuery({ foo: 12, bar: "#user->here", object: { test: "test", bird: { super: { ultra: { mega: { deep: "human" }, shop: 7 } }, multiple: [1, 2, 3] } } }))
Result
foo=12&bar=%40user-%3Ehere&object_test=test&object_bird_super_ultra_mega_deep=human&object_bird_super_ultra_shop=7&object_bird_multiple=1%2C2%2C3
or
foo: 12
bar: #user->here
object_test: test
object_bird_super_ultra_mega_deep: human
object_bird_super_ultra_shop: 7
object_bird_multiple: 1,2,3
Conclusion
We got different snippets you can choose from depending on your goals.

Property not being added to JS object

So I am trying to add a field to a user object right before I return it and the property is not being added for some reason. Any thoughts?
returnUser = function(userRes) {
console.log(">POST /returnuser< Returning user w/ userId: " + userRes.userId + " deviceToken: " + userRes.deviceToken);
return Location.findOne({
users: {
$elemMatch: {
user: userRes.userId
}
}
}, function(error, response) {
userRes.currentLocation = response;
console.log(userRes);
return res.send(userRes);
});
};
So in the returnUser function I am searching the DB for the user's current location, adding that to the userRes object and then returning it. But when I log the userRes object it doesn't contain that property. Any issue with my code? userRes currently looks like this:
{ _id: 1,
createTime: 1428477183281,
deviceId: '982f24khsd',
deviceToken: 'kjs398fskjjg5fb43ds323',
firstName: 'Cat',
lastName: 'Man',
email: 'cat#mandu.com',
__v: 0,
users: [],
locations: [],
lngLat: [] }
As it turns out, that userRes object is actually a mongoose model instance returned from a mongoose query which is not mutable.
To fix this you can preferably call lean() which will give you a plain JS object instead of a full model instance.
Or if you don't have access to the query you can do something like:
mutableUserRes = JSON.parse(JSON.stringify(userRes));
Which copies the whole object but it is now a simple JS object instead of a mongoose model instance.

Adding to a JSON string

I have a JSON string as follows:
[
{"TypeName":"Double","TypeID":14},
{"TypeName":"Single","TypeID":43},
{"TypeName":"Family","TypeID":7}
]
It is generated after calling this function in KnockOut:
self.save = function() {
var dataToSave = $.map(self.lines(), function(line) {
return line.product() ? {
TypeName: line.category().TypeName,
TypeID: line.category().TypeID
: undefined
});
alert(JSON.stringify(dataToSave));
However, I want to add 3 more pieces of information to the model, before posting it back to my server - to also send Name, Email and Tel:
{
"Name":"Mark",
"Email":"me#me.com",
"Tel":"0123456789",
"Rooms":
[
{"TypeName":"Double","TypeID":14},
{"TypeName":"Single","TypeID":43},
{"TypeName":"Family","TypeID":7}
]
}
Is there a proper way of adding this information to the JSON, or is it just as simple as:
var toSend = "{\"Name\":\"Mark\":\"Email\":\"me#me.com\", \"Tel\":\"0123456789\",\"Rooms\":"
+ JSON.stringify(dataToSave) + "}";
Thank you,
Mark
Parse your JSON string using JSON.parse into a valid JS object, add the data to the object as needed, then JSON.stringify it back. A JSON string is just a representation of your data, so you shouldn't rely on modifying it directly.
Why encode to JSON and then modify the resulting string when you can pass the structure you actually want to the JSON encdoder?
var toSend = JSON.stringify({
Name: "Mark",
Email: "me#me.com",
Tel: "0123456789",
Rooms: dataToSave
});

Categories

Resources