Cannot Delete Fields with Period in Key Name - javascript

I've tried every solution in this other StackOverflow thread, and none of them have worked for me. At this point I'm absolutely stumped, and have no idea on what to try next.
The data that I'm trying to access is this:
The key I'm trying to use is a URL, which is inside the accounts map. This is the code I'm running to try and delete the key:
var userRef = db.collection('userAccounts').doc(userEmail)
let dynamicKey = `accounts.${accountURL}`
console.log(dynamicKey)
userRef.set({
[dynamicKey]: firebase.firestore.FieldValue.delete()
}, { merge: true})
.then((result) => {
console.log(result)
})
.catch((error) => {
console.log(error)
})
Looking in console, accounts.www.stackoverflow.com is printed:
So, it looks like the path should match. A note to make is that a URL with no periods works just fine, so it seems like the path IS correct, and that periods are in fact the issue.
The accepted answer by J Livengood simply doesn't work for keys with periods in the name:
[`hello.${world}`]: firebase.firestore.FieldValue.delete()
The code posted by Sam Stern simply doesn't run, and I get an error mentioning that update only takes one parameter. Contrary to the last poster (ishandutta2007), adding a 'new' before the FieldPath doesn't fix the error:
doc.update(
firebase.firestore.FieldPath("hello.world"),
firebase.firestore.FieldValue.delete());
This code (in the comments) posted by OP, Sandeep Dinesh, just doesn't work at all, even when trying to delete with a key with no period. My code is the following, and the returned Promise is undefined in the "then" portion of the code:
var userRef = db.collection('accounts').doc(userEmail)
let dynamicKey = `accounts.${accountURL}`
userRef.set({
[dynamicKey]: firebase.firestore.FieldValue.delete()
}, { merge: true})
.then((result) => {
console.log(result)
})
.catch((error) => {
console.log(error)
})

The problem you have here is a little bit different than you described. Specifically, you're trying to delete a nested map key with periods in it. It's important to realize that this nesting requires special treatment that you didn't see in other questions, which were dealing only with top-level fields.
You will need to use FieldPath here, and specify the path of the field as array elements in the constructor. From the linked API documentation (emphasis mine):
Creates a FieldPath from the provided field names. If more than one field name is provided, the path will point to a nested field in a document.
So, you should set or update with a field of new firebase.firestore.FieldPath(["accounts", "www.stackoverflow.com"]) and a value of firebase.firestore.FieldValue.delete().
documentReference.update(
new firebase.firestore.FieldPath(["accounts", "www.stackoverflow.com"]),
firebase.firestore.FieldValue.delete()
);

Related

cy.wrap().its()... doesn't work when the value in .its() contains a period

I am looking to extract a URL parameter from the current URL I'm testing with Cypress. I was able to basically get the answer from this SO post, however, my extracted values are not available to me when I use Cypress's .its() command.
The parameters in the url all have periods in them, and I believe this is the cause for my error.
Here is my custom Cypress Command I'm building:
Cypress.Commands.add('getParmsCommand', function(value) {
cy.url().as('url')
cy.then( () => {
cy.log(this.url)
const kvPairArray = this.url.toString().split('?')[1].toString().split('&')
const paramObj = {}
kvPairArray.forEach(param => {
cy.log(param)
//default 'value' to 0 if it doesn't exist
const [ key, value="0" ] = param.split('=')
paramObj[key] = value
})
//forcefully adding a debug element to the key value store for testing
paramObj['beverage'] = 'soda'
cy.wrap(paramObj)
.its('timeline.ws') //doesn't work
// .its(`${Cypress.$.escapeSelector('timeline.ws')}`) doesn't work
// .its('timeline\.ws') doesn't work
// .its('"timeline.ws"') doesn't work
// .its('beverage') this DOES work!
.then(parmVal => {
cy.log(parmVal)
})
Here is the relevant part of the URL that I'm trying to extract from:
timeline.ws=3600000&timeline.to&timeline.fm&timeline.ar=false
You can see from the error that Cypress is only looking for the id timeline, NOT timeline.ws; it completely ignores everything after the period, and thus, never finds my parameter.
I saw there was a similar error with Cypress's .get() function back in 2018.
I am new to both javascript and Cypress, so I hope it's just a weird easy thing I'm overlooking. Any advice or educated guesses are greatly welcome at this point!
Thank you.
.its() is just a shorthand for property extraction. Since it fails with the period, you could instead use bracket notation in a .then().
cy.wrap(paramObj)
.then(paramObj => paramObj['timeline.ws'])
or just
cy.wrap(paramObj['timeline.ws'])
Playing around with the URL constructor
const urlString = 'http://example.com?timeline.ws=3600000&timeline.to&timeline.fm&timeline.ar=false'
const url = new URL(urlString)
cy.wrap(url.searchParams.get('timeline.ws'))
.should('eq', '3600000')
cy.wrap(url.searchParams.get('timeline.to'))
.should('be.empty')
cy.wrap(url.searchParams.get('timeline.ar'))
.should('eq', 'false')

Mongoose update map element inside array of document

as the title says, I want to update a value in a map inside an array in a document, Im using Mongoose. This is for a Discord bot, basically adding exp to a char every time user does a command, and it works, but it doesn't look right and I've been told it doesn't work sometimes:
This is how the maps inside the array look like:
click
// This gets the user document
let user = bot.userinventories.findOne({ userID: message.author.id })
// This searches the specific map I need by name
let check = await user.get('characters').filter(char => char.name === user.get('leveling'));
// This modifies the map in only 1 value
check[0].exp++;
// This removes the entire map and saves it again
await bot.userinventories.updateOne({ userID: message.author.id },
{ $pull:
{
characters: { name: check[0].name }
}
})
await bot.userinventories.updateOne({ userID: message.author.id },
{ $push:
{
characters: check[0]
}
})
As you can see, the part that doesn't seem right is having to entirely remove the map before saving it again, this also breaks any order by date. The big problem is that users have told me they've lost characters to this.
Another thing that sometimes fails is the 'let check' line, even tho it finds a map by the exact name (because of my code) it fails randomly like 1/20 times.
I found this which works, even tho it stills moves the object to the last place of the map, but it seems cleaner than what I had:
await bot.userinventories.updateOne(
{ userID: message.author.id, "characters.name": check[0].name },
{ $set: { "characters.$.exp" : check[0].exp } }
)
Edit: for it to not move the edited object to last place, I had to make a new variable or not use a variable for '"characters.$.exp" : ' for some reason

More efficient way to handle an extremely specific response from a Rest API in JavaScript

I'm working with a Dungeons & Dragons 5e API and want that an especific result be treated in a special way. The user can choose what to search from a range of options, and in only one of them would I need to take care of the answer in a different way. In this option, I get the answer in JSON that contains a 'name' field, which stores a String, but in this specific case this String comes with an acronym, and I would like to transform it into the full name.I'm afraid to just put am 'if' statement in the middle of the code and deal with the situation inefficiently, even more so that I did not find similar situations to have any reference.
This is part of the result of the API I want to handle in a special way:
{"count":6,
"results":[
{"name":"STR",
"url":"http://www.dnd5eapi.co/api/ability-score/1"},
{"name":"DEX",
"url":"http://www.dnd5eapi.co/api/ability-scores2"},
....
]
}
This is how I handle the answer:
fetch(fullAPIURL)
.then(result => result.json())
.then(data => {
let resultContainer = document.getElementById('resultContainer');
//Cleaning the result container from previous results
document.querySelectorAll('#resultContainer article').forEach(container =>
resultContainer.removeChild(container));
spanSearchResult.classList.remove('invisible', 'searchFail');
spanSearchResult.classList.add('searchSucess');
spanSearchResult.innerHTML = `Search returned ${data.count} results`;
for (element of data.results) {
let containerTitle = element.name != undefined ? element.name : element.class;
resultContainer.appendChild(createResultContainer(containerTitle));
}
})
.catch(err => {
spanSearchResult.classList.remove('invisible');
spanSearchResult.classList.add('searchFail');
spanSearchResult.innerHTML = 'Something went wrong! Details in the console';
console.log(err);
});
Is putting a condition in this snippet of code really the most efficient way to solve this situation?
Thanks in advance.
You could just make a lookup call, actually. In fact, that'd be preferable if you ever want to port your application to another language, for example.
Define the following:
var retrieve = (function() {
var items = {
"STR": "Strength",
"DEX": "Dexterity"
};
return function(item) {
return items[item] || item;
}
})();
console.log(retrieve("DEX"));
With this, you can simply call retrieve(element.name) to retrieve its "actual" name. You can add elements to the object to create new translations, and if you ever need to support multiple languages, you can even replace the function entirely.

Firebase functions reading/accessing remaining node information

I have create a trigger to monitor the path /Messages/{pushId}/originalText, so this should only trigger if a change occurs at that specific node and nowhere else.
What I would like to do is access the remaining Node data so for example how would I need the node date at the /Messages/{pushId}/followers which sits at the same level as originalText
Sample:
exports.makeUppercase = functions.database.ref('/Messages/{pushId}/originalText')
.onWrite(event => {
//how to access data at another node, for example
//important/Messages/{pushId}/followers
})
exports.makeUppercase = functions.database.ref('/Messages/{pushId}/originalText')
.onWrite(event => {
event.data.ref.parent.child('followers').once('value', data => {
console.log('Your data: ' + data)
})
})
I suggest you to take a look at the documentation it is really nice and you can find everything you want!
onWrite's documentations says that .onWrite returns a DeltaSnaphsot
DeltaSnaphot's doc says that DeltaSnaphot.ref() returns a Reference
Reference's documentation has every query method you need, in this case once

Word: Update a TableRow using the Word JavaScript Api

I'm having difficulties to update the values of a Word.TableRow using the Javascript API. I've look to Doc and I can't find any hints that will help me to accomplish my duty...
Here is my Question: What is the best way to set the values of a TableRow inside a Word Document using the Javascript API.
And Here is what I tried:
Word.run((context: Word.RequestContext) => {
var tables = context.document.body.tables;
tables.load("items");
return context.sync().then(() => {
var firstTableRows = tables.items[0].rows;
context.load(firstTableRows, "items");
context.sync().then(() => {
var header = firstTableRows.items[0];
header.load("values");
context.sync().then(() => {
header.values = ["MyValue"]
context.sync();
});
});
});
}).catch(errorHandler);
This is a 1x2 table
No errors are thrown and the table is not getting updated...
Am I missing something?
Sorry for the delayed response here. There are a couple of issues with your code.
the first one is associated with a bug we are currently fixing with keeping references of items mentioned in the load method. There is a related question on stack explaining this issue. Check it out here. (specially the detailed explanation in the comments).
The second issue I see with your code is that you are trying to set the row values using a 1d array, when a 2D array is expected. (and this is an error in our documentation, we need to fix it thanks to you!)
You have a couple of options for changing values, this is the first one:
Word.run(function (context) {
var myTables = context.document.body.tables;
context.load(myTables);
return context.sync()
.then(function () {
var myRows = myTables.items[0].rows;
context.load(myRows);
return context.sync()
.then(function () {
//assumes a table with at least 3 columns!
myRows.items[0].values = [["1", "3", "4"]];
return context.sync()
})
})
})
.catch(function (e) {
app.showNotification(e.message);
})
}
Now, until the bug mentioned in the first point is fixed you need to do several trips to the host application in order to set the data you want. a potential shortcut is to use our new navigation options,specially if you want to change the first or last row values (please note that we are planning to rename the first to getFirst() by the time we ship the API, so this code will break in a couple of months, maybe already breaks if you are using the insider fast release), you can do something like this in just one call:
Word.run(function (context) {
//this gets the first row of the first table in the document and sets its values. Please note the 2D array used!
context.document.body.tables.first.rows.first.values =[["Juan", "Peter", "Jeff"]];
return context.sync()
.catch(function (e) {
app.showNotification(e.message);
})
})
Please give it a try to these options and let me know how it goes.
thanks!

Categories