Shopify get all products - javascript

I am creating an application and at the moment I am trying to get all product data from the store, but the function does not work for me.
I am making a request for this feature, but in the response I have no 'link' at all. And there is no other replacement that would link to the next page.
function makeRequest(
nextLink = 'https://myshopname.myshopify.com/admin/api/2021-04/products.json?limit=1'
) {
return new Promise((resolve, reject) => {
console.log(nextLink);
fetch(nextLink).then((r) => {
const headerLink = r.headers.get('link');
console.log(r.headers);
console.log(headerLink);
const match = headerLink.match(/<[^;]+\/(\w+\.json[^;]+)>;\srel="next"/);
const nextLink = match ? match[1] : false;
if (nextLink) {
makeRequest(nextLink);
} else {
resolve();
}
});
});
}
Can you help me figure out how I can make this function work and how can I get all the data from this store?

If someone were looking for an answer to the same question, the answer turned out to be very simple. For this feature to really work, you need a Shopify Plus store subscription. If you have it, but the code still does not work, then you need to contact Shopify technical support and write to them about this problem.
If you need to get all the products from the store, then I recommend using another way to get data, namely through GraphiQL.

Related

API can't handle my request because of template literals to make the API dynamic

For a school project, I have to make a quiz app. It is possible to chose a difficulty, a category and an amount of desired questions. The api is a url which can be modified easily by changing some values. For example: https://quizapi.io/api/v1/questions?apiKey=MYAPIKEY&limit=15&difficulty=hard&category=cms. If you would just change the php to code in the url, you would get a max amount of 15 questions on a hard difficulty about HTML and CSS. I think you see where this is going.
However. I have setup my code that the difficulty, category and amount are stored in localstorage and they are fetched when the quiz is started. At the moment, I get the amout of questions I desire but I can't change my difficulty or category because probably Template Literals aren't working in a fetch api.. Maybe someone can give me an idea or maybe I'm making a mistake in my current code
let storageDif = localStorage.getItem("mD");
console.log(storageDif.toString());
let storageCat = localStorage.getItem("mC");
console.log(storageCat);
let geslideVragen = localStorage.getItem("slider");
let MAX_VRAGEN = geslideVragen;
console.log(MAX_VRAGEN);
let vragen = [];
fetch(`https://quizapi.io/api/v1/questions?apiKey=kAFKilHLeEcfLkGE2H0Ia9uTIp1rYHDTIYIHs9qf&limit=15&difficulty=hard&category=${storageCat}`)
.then((res) => {
return res.json();
})
.then((loadedQuestions) => {
for (let i = 0; i < MAX_VRAGEN; i++) {
vragen = loadedQuestions;
console.log(vragen[i].question);
};
startGame();
})
.catch( err => {
console.error(err);
});
I'm sure you found out by now that you're only interpolating the category. To get it to be correctly, you'd need to do this:
`https://quizapi.io/api/v1/questions?apiKey=kAFKilHLeEcfLkGE2H0Ia9uTIp1rYHDTIYIHs9qf&limit=${MAX_VRAGEN}&difficulty=${storageDif}&category=${storageCat}`
That being said, you should never expose your API keys this way, because especially for cloud services, it can easily cost you over 5 digits in a single day if someone decided to use it for their own means. There are plenty of scrapers that scour GitHub for exposed API keys for illegitimate uses.
Also, should apply a check to make sure all values are present using an if() statement so that it doesn't fetch anything if a value is undefined.

Liferay 7: Using URL params and Javascript to prefill a form

I've been working with Liferay 7 for a while and needed to create a feedback form with prefilled values. I created a feedback form and a page, where it's shown and where I could add Javascript.
The user clicks on a link ("Did you find this helpful? Yes/No") and it takes you to the feedback page with the page and answer as URL parameters.
URL: {feedback-page-url/} ?pageUrl=/services&answer=Yes
Now here's where the problems began. Liferay updates it's values very confusingly and while generic document.getElementsByName(...) etc. seemed to work at first, they updated back when clicking the page. The difficult thing is to update the right values in right elements, so they won't be overrun by Liferay.
I provided an answer to my question below. Feel free to ask me anything, I'll try to help :)
Full code block in the end!
So I found out a solution to this problem. Liferay creates an instance (_com_liferay...) and uses it's values to be up to date, so we need to get a hold of it and update it's values. You can do it manually by inspecting and finding your instance, but I have an automatic code that should get it for you.
The id we are searching for is for DDMFormPortlet and the String we get this way is close to perfect. The String that document.querySelector() finds begins with p_p_id_com..., so we can use .substring to remove the unnecessary part and then add +"form" in the end to make it complete. If you find a better way to find this, please share it :)
// _com_liferay_dynamic_data_mapping_form_web_portlet_DDMFormPortlet_INSTANCE_randomkey_form
const idFinder = function() {
const idString = document.querySelector('[id*="DDMFormPortlet"]').id;
return(idString.substring(6) + "form");
}
Now that we have the correct String text, we'll find the element, that corresponds to it:
const formFieldArray = window[idFinder()];
Now if you try it just by itself, it most likely won't find anything, because it's loads slowly. I put all of this into try-catch with setTimeout() to make sure everything works as intended. Now all we need to do is collect the information from our URL and set it to the correct places.
const params = new URLSearchParams(location.search);
const formAutoFiller = function (params) {
try {
const formFieldArray = window[idFinder()];
// make sure you have the numbers according to your form!
formFieldArray.pages[0].rows[0].columns[0].fields[0].value=params.get('pageUrl');
formFieldArray.pages[0].rows[1].columns[0].fields[0].value=params.get('answer');
// ...
}
}
And finally, as the changed values update to the form after clicking an input field, we'll move the selection focus to one of the input fields after the other code is ran:
document.getElementsByClassName("ddm-field-text")[1].focus();
A bit cleanup for myself and we're done! Full Javascript here:
const params = new URLSearchParams(location.search);
const idFinder = function() {
const idString = document.querySelector('[id*="DDMFormPortlet"]').id;
return(idString.substring(6) + "form");
}
const formAutoFiller = function (params) {
try {
const formFieldRows = window[idFinder()].pages[0].rows;
formFieldRows[0].columns[0].fields[0].value=params.get('pageUrl');
formFieldRows[1].columns[0].fields[0].value=params.get('answer');
document.getElementsByClassName("ddm-field-text")[1].focus();
} catch (e) {
setTimeout(formAutoFiller, 500, params);
}
}
formAutoFiller(params);

How can I repeat a Firestore query until a condition is met?

So I am working on a storing users information in my database and in addition to the standard data I get from a Firebase User, I wanted to add my own custom data. I am setting up a profile page for each of my users and I wanted to generate a random 10 digit number to be their profileID. I then store it in my database just in case I need it. However, I need them to be unique numbers and for them not to repeat so I'm trying to query the database to check if the profileID that I generated is already in use and if so, generate another one. However, I can't figure out how to repeat a query in Firestore other than maybe using a loop but that doesn't seem like it would work.
const usersRef: AngularFirestoreCollection = this.afs.collection('users');
var query = usersRef.ref.where("profileID", '==', profileID);
query.get().then((querySnapshot) => {
if (!querySnapshot.empty) {
profileID = this.getRandomProfileID();
}
})
A recursive function like this seems the way to go:
function getRandomProfileID() {
return new Promise(function(resolve, reject) {
var profileID = ....
var query = usersRef.ref.where("profileID", '==', profileID);
query.get().then((querySnapshot) => {
if (!querySnapshot.empty) {
this.getRandomProfileID();
}
else {
resolve(profileID);
}
})
})
}
And then you'd call it with:
getRandomProfileID().then(function(profileID) {
console.log(profileID);
});
Or if you're using a more modern version of JavaScript:
var profileID = await getRandomProfileID();
console.log(profileID);
As Doug pointed out, with async/await you can even do without the recursive function entirely:
while (true) {
let profileID = ....
let snapshot = await usersRef.ref.where("profileID", '==', profileID).get()
if (!snapshot.empty) {
break;
}
}
console.log("Found unique ID: " + profileID);
But there are some things still to consider with this approach:
Another user may generate the same profile ID between the time you generate and check it, and actually creating it. If this is a concern, you should probably use a transaction for the function above, and have it create the profile document. If it's not a realistic concern, you should still ensure in security rules that no user can ever overwrite a document that was already created by another user.
If you're going to use your own profile ID in a lot of places to identify the user, consider creating an additional collection where each document name/ID is the profile ID, and the document contents are the key into the users collection. If you're going to uniquely identify the users by their profile ID, consider using the profile ID as the key of their document in the users collection, instead of whatever key you know use.

getAllData does not include element just inserted, even after callback

So I am using NeDB as a data store for a simple little project, and I want to create a little API that inserts something new and returns the list of all items in the datastore, including the new one.
async function willAddNewItem() {
// db is NeDB Datastore
const existing = db.getAllData();
const sortedIds = existing
.map(item => item.id)
.sort((one, another) => one - another);
const id = sortedIds.length === 0
? 0
: sortedIds.slice(-1)[0] + 1;
const newItem = { id, foo: 'bar' };
await new Promise(resolve => db.insert(newItem, () => resolve()));
return db.getAllData()
.sort((one, another) =>
new Date(one.updatedAt).getTime() - new Date(another.updatedAt).getTime()
);
}
However, every time I call this function, I get the list of items I had before inserting the new one. On the next call, the item I added last time would be there, but not the new one. Refreshing my page (which results in calling db.getAllData() again to populate the initial page) shows everything it should. It’s only missing immediately after I insert it. So it would appear that the callback on db.insert is not actually waiting until the insertion is complete (nor is there any documentation that I can find about this).
Adding an index to id fixes this, and I do want an index on id so that is good, but I still want to know why this is happening/where I have to be careful about it happening in the future. For that matter, I’m a little worried that the only reason adding the index to id worked is because it happened to be a little faster, so it was “done” before I called db.getAllData() when it wasn’t before—and that this may not be consistent.
I could use something like
const afterAdding = await new Promise(resolve =>
db.insert(newItem, (_, newDoc) => resolve([...existing, newDoc]))
);
and return the sorted afterAdding, but this seems a little dubious to me (I’d really like to get exactly the db’s state rather than try to “recreate” it myself, assuming that insert has done exactly and only what I expected), and in any event I would like to know more about how NeDB works here.
Also, yes, I know I’m ignoring possible errors on the insert (I have checked to see if this is the cause; it is not), and I probably don’t want .getAllData() at all but rather some .find query. This is a basic test case as I get familiar with NeDB.

Get specific value from Firebase JSON tree based on value

I am writing a Javascript web app using Firebase. If I have data like this in my JSON tree:
users
session1
screenname:Bill
session2
screenname:Steve
...and I know the session number, how can I grab the "name" of the user? I have tried this:
valUsers.child('session1').child('screenname').once('value', function(data){
console.log(data);
});
But that does not seem to work. I'm guessing I have a syntax issue?
You were pretty close, but needed to use DataSnapshot.val() as shown below to access the point you want.
var ref = new Firebase('yourfirebaselocation/users');
var sessionNum = 'session1';
ref.child(sessionNum + '/screenname').once('value', function(dataSnapshot) {
var specificScreenname = specificPoint.val();
}
When working with Firebase, you may want to also explore working with Promise objects. This would allow you to create some repeatable functions that can reuse your reference on multiple locations and also give Firebase time to respond to your query.
var ref = new Firebase('yourfirebaselocation/users');
function getUser(theRef, location) {
return new Promise(
function(resolve, reject) {
theRef.child(location).once('value', function(dataSnapshot) {
resolve(dataSnapshot);
});
});
}
getUser(ref, specificlocationvariable).then( function(val) {
specificScreenname = val.val();
});
You don't specify other than 'JavaScript' so do keep in mind that Promises are an ECMAScript 6 proposal with some compatibility issues, but a lot of the JS frameworks have an implementation for it that smooth that issue (Angular, Ember, etc).

Categories