dynamically gerDownloadURL from images on firebase storage for display - javascript

I have a whole bunch of images that have been uploaded on firebase storage and I want to dynamically retrieve the images and display inside my app screen. This is what I tried so far without success:
I tried out the listFilesAndDirectories function found in the RN firebase storage usage API reference which gives me this error:
Possible Unhandled Promise Rejection (id: 1):
Error: [storage/object-not-found] No object exists at the desired reference.
NativeFirebaseError: [storage/object-not-found] No object exists at the desired reference.
function listFilesAndDirectories(reference, pageToken) {
return reference.list({pageToken}).then(result => {
// Loop over each item
result.items.forEach(ref => {
console.log(ref.fullPath);
});
if (result.nextPageToken) {
return listFilesAndDirectories(reference, result.nextPageToken);
}
return Promise.resolve();
});
}
const storageReference = storage()
.ref('gs://appname445.appspot.com/images');
listFilesAndDirectories(storageReference).then(() => {
storageReference.getDownloadURL();
console.log('Finished listing');
});
the above function prints the log statement "Finished listing' but doesn't display image
I also wrote this function which didn't work, it outputs a maxDownloadRetryError after 3 minutes
function fetchImage() {
reference.getDownloadURL().then(
function(url) {
console.log(url);
},
function(error) {
console.log(error);
},
);
}
fetchImage();

The error message is telling you there is no object at the location of the reference you're using. It's not possible to use getDownloadURL() with a path that isn't an actul file object. You can't use it on prefixes (folders).
If you're trying to get a download URL for each object that you listed with listFilesAndDirectories, you would have to call getDownloadURL() on each and every file object that it finds (not just once for the entire prefix).
It would be more like this:
function listFilesAndDirectories(reference, pageToken) {
return reference.list({pageToken}).then(result => {
result.items.forEach(ref => {
// call getDownloadURL on every object reference
ref.getDownloadURL().then(url => {
console.log(`${fullPath}: ${url}`)
})
});
if (result.nextPageToken) {
return listFilesAndDirectories(reference, result.nextPageToken);
}
return Promise.resolve();
});
}

Related

Getting error on Firebase - FirebaseError: Invalid segment (users//cart). Paths must not contain // in them

Code in cart.component.ts
this.cart.getCart().subscribe(cs =>
{
this.shoppingCart = cs.map( x =>{
return{
id: x.payload.doc.id,
...x.payload.doc.data() as {}
}
})
}
code in cart.service.ts
getCart(){
return this.fs.collection(`users/${this.as.userId}/cart`).snapshotChanges()
}
The only possibility I can see here is that this.as.userId is empty. Make sure that value is not empty, and that should resolve the issue. Because it's empty, your getCart() function is trying to query a collection users//cart, but there should be a user id between the users/ and /cart parts.

Accessing non-existent property of module.exports inside circular dependency NodeJS

Im having some issues when using module.exports inside NodeJS, and I've followed multiple guides, and im almost certain Im doing it right.
I have to scripts, main.js and event.js. Im trying to share a function from main.js to event.js, but its not working. Here is the code:
Main.js
function Scan(){
if(fs.readdirSync('./events/').length === 0){
console.log(colors.yellow('Events Folder Empty, Skipping Scan'))
} else {
var events = fs.readdirSync('./events/').filter(file => file.endsWith('.json'))
for(const file of events){
let rawdata = fs.readFileSync('./events/' + file);
let cJSON = JSON.parse(rawdata);
}
events.sort()
tevent = events[0]
StartAlerter()
}
}
module.exports = { Scan };
Event.js
const main = require('../main')
main.Scan;
This returns the error:
(node:19292) Warning: Accessing non-existent property 'Scan' of module exports inside circular dependency
(Use `node --trace-warnings ...` to show where the warning was created)
What am I doing wrong?
I discovered that the arrangement had no effect in the error.
I simply changed the way I exported the function from the Main.js
from:
module.exports = { Scan };
to:
exports.Scan = Scan
And in Event.js, I was able to access the file like this
const main = require("./Main.js");
let result = main.Scan();
This solved my problem, I hope it helps another developer 😎
Problem solved, heres what I did differently:
module.exports = { Scan };
Is declared before the Scan function is defined, like so:
module.exports = { Scan };
function Scan(){
//Code
}
Then in event.js, I wrote
const main = require('../main')
As it is now a module, and can be used with the require() function.
Then to execute the funciton in event.js, I write
main.Scan()
To execute it.
better try :
module.exports = Scan;
I am gonna answer it using a simple example, like in this case below:
File A has 3 functions to process database activity: function
addDB, updateDB, and delData;
File B has 2 functions to process User activity on smartphone:
function addHistory, and editHistory;
Function updateDB in file A is calling function editHis in file B, and function editHistory is calling function updateDB in file A. This is what we called circular-dependency. And we need to prevent it by only giving output of state from editHistory and the rest will be processed inside file A.
//ORIGINAL FUNCTIONS which caused CIRCULAR DEPENDENCY
function updateDB() {
//process update function here
//call function in fileB
const history = require("fileB.js");
await history.editHistory(data).then((output) => {
if(output["message"] === "success"){
response = {
state: 1,
message: "success",
};
}
});
return response;
}
//THIS is the WRONG ONE
function editHistory() {
//process function to edit History here
//call function in fileA
const file = require("fileA.js");
await file.updateDB(data).then((output) => { //You should not call it here
if(output["message"] === "success") {
output = {
state: 1,
message: "success",
};
}
});
return output;
}
//==================================================//
//THE FIX
function updateDB() {
//process function here
const history = require("fileB.js");
await history.editHistory(data).then((output) => {
if(output["message"] === "success"){
await updateDB(data).then((output) => {
response = {
state: 1,
message: "success",
};
});
} else {
log("Error");
}
});
return response;
}
function editHistory() {
//process function to edit History here
// No more calling to function inside the file A
output = {
state: 1,
message: "success",
};
return output;
}

Data fetched from API correctly; app gives TypeError with 'undefined' variable while processing with 'computed'

I ran into troubles trying to process data fetched from remote API.
The app is running VueJS with Vuetify, data is formatted with Vuetify's data table component.
This is my code:
export default {
data () {
return {
headers: [
{ text: 'City', value: 'city' },
{ text: '#Citizens', value: 'citizens' },
{ text: '#Schools', value: 'schools' },
{ text: 'Schools per Citizen', value: 'schoolsPerCitizen' },
(...)
API URL is defined as a variable on the root level of the app.
Then, there is this method launched when created() kicks in:
methods: {
loadData() {
axios.get(citiesApiUrl)
.then((response) => {
console.log(response.data) // data displayed correctly
return response.data
})
.catch(error => {console.error(error)})
}
},
created () {
this.loadData()
}
As you noticed in the comment, response.data does display desired values.
Problems start from this point:
computed: {
stats() {
return this.loadData().map(item => {
item.schoolsPerCitizen = (item.schools / item.citizens).toFixed(2)
return item
})
}
}
I get an error: TypeError: Cannot read property 'map' of undefined.
Any ideas what is wrong with my code?
Issues
When loadData is called in created, the axios promise is consumed but nothing happens with the returned data except it's logged and returned to the promise resolver.
When loadData is called in stats (computed), .map is chained off of the return value from loadData, but loadData has no return value.
Even if loadData returned the axios promise, that promise would have to be consumed in stats first before accessing the data (needs .then)
The design is flawed because the computed will make an identical API call every time it recalculates, which is likely unnecessary.
Also, the promise returned by stats wouldn't be resolved by the template render function anyway.
Fix
Create a variable for the loaded data (I'll call it mydata):
data() {
return {
// ...
mydata: []
}
}
Change loadData to:
loadData() {
axios.get(citiesApiUrl).then((response) => {
this.mydata = response.data // <--- Set the data to `mydata`
}).catch(error => {
console.error(error)
})
}
Change stats to:
stats() {
// This is also not designed properly, it's going to mutate `mydata`...
// You should study Vue and learn what the purpose for computeds are before using them
return this.mydata.map(item => { // <-- Once `mydata` is populated, this will recalculate
item.schoolsPerCitizen = (item.schools / item.citizens).toFixed(2)
return item
})
}
loadData does not return any value.
loadData() {
return axios.get(citiesApiUrl)
.then((response) => {
console.log(response.data) // data displayed correctly
return response.data
})
.catch(error => {console.error(error)})
}

How I can get access to API in the global command file?

I want to create a global command in the nightwatch.js framework when I do that in page object_file without global command function
navigateWithNav() {
return navigateWithNavToolbar.call(this, "#home-nav")
},
Everything works correctly. But when I trying change function in the object _file, on global command I will get undefined for this.api, how I can resolve it?
// page_oject file
navigateWithNav() {
return this.navigateWithNavToolbar("#home-nav")
},
// global command file
const { client } = require("nightwatch-cucumber")
const { MID_TIMEOUT } = global.config.timeouts
exports.command = async function navigateWithNavToolbar(selector) {
return this.api.url(async (url) => {
// if we are someplace which doesnt have the nav toolbar, then
// goto the homepage
if (!url.value.includes(client.launch_url)){
await client.url(client.launch_url)
}
await this.api.waitForElementPresent(selector, MID_TIMEOUT, false)
await this.api.click(selector)
})
}
I don't know about the syntax that the page_oject file code is in, but you want to do something like this to bind this:
navigateWithNav = () => this.navigateWithNavToolbar("#home-nav")

Angular subscribe within subscribe: data doesn't load at the same time within view

I know it is bad practice to call subscribe within subscribe but I don't know how to handle it differently with my special case.
The code as it is now works, but my problem is that if I update my website for example every second, parts of the table are loaded first and other parts are loaded afterwards (the content of the subscibe within my subscribe).
I have a service containing a function that returns an Observable of a list of files for different assets.
Within that function I request the filelist for each asset by calling another service and this service returns observables.
I then iterate over the elements of that list and build up my data structures to return them later on (AssetFilesTableItems).
Some files can be zip files and I want to get the contents of those files by subscribing to another service (extractZipService). To be able to get that correct data I need the name of the file which I got by requesting the filelist. I then add some data of the zip contents to my AssetFilesTableItems and return everything at the end.
The code of that function is as follows:
getAssetfilesData(assetIds: Array<string>, filter: RegExp, showConfig: boolean): Observable<AssetFilesTableItem[][]> {
const data = assetIds.map(assetId => {
// for each assetId
return this.fileService.getFileList(assetId)
.pipe(
map((datasets: any) => {
const result: AssetFilesTableItem[] = [];
// iterate over each element
datasets.forEach((element: AssetFilesTableItem) => {
// apply regex filter to filename
if (filter.test(element.name)) {
this.logger.debug(`Filter ${filter} matches for element: ${element.name}`);
// build up AssetFilesTableItem
const assetFilesItem: AssetFilesTableItem = {
name: element.name,
type: element.type,
asset: assetId
};
// save all keys of AssetFilesTableItem
const assetFilesItemKeys = Object.keys(assetFilesItem);
// if file is of type ZIP, extract 'config.json' from it if available
if (showConfig && element.type.includes('zip')) {
this.extractZipService.getJSONfromZip(assetId, element.name, 'config.json')
.subscribe((configJson: any) => {
const jsonContent = JSON.parse(configJson);
const entries = Object.entries(jsonContent);
entries.forEach((entry: any) => {
const key = entry[0];
const value = entry[1];
// only add new keys to AssetFilesTableItem
if (!assetFilesItemKeys.includes(key)) {
assetFilesItem[key] = value;
} else {
this.logger.error(`Key '${key}' of config.json is already in use and will not be displayed.`);
}
});
});
}
result.push(assetFilesItem);
}
});
return result;
}));
});
// return combined result of each assetId request
return forkJoin(data);
}
}
I update my table using the following code within my component:
getValuesPeriodically(updateInterval: number) {
this.pollingSubscription = interval(updateInterval)
.subscribe(() => {
this.getAssetfilesFromService();
}
);
}
getAssetfilesFromService() {
this.assetfilesService.getAssetfilesData(this.assetIds, this.filterRegEx, this.showConfig)
.subscribe((assetFilesTables: any) => {
this.assetFilesData = [].concat.apply([], assetFilesTables);
});
}
Edit: I tried ForkJoin, but as far as I understandit is used for doing more requests in parallel. My extractZipService though depends on results that I get from my fileService. Also I have a forkJoin at the end already which should combine all of my fileList requests for different assets. I don't understand why my view is not loaded at once then.
EDIT: The problem seems to be the subscribe to the extractZipService within the forEach of my fileService subscribe. It seems to finish after the fileService Subscribe. I tried lots of things already, like SwitchMap, mergeMap and the solution suggested here, but no luck. I'm sure it's possible to make it work somehow but I'm running out of ideas. Any help would be appreciated!
You are calling this.extractZipService.getJSON inside a for loop. So this method gets called asynch and your function inside map is not waiting for the results. When result does come as your items are same which is in your view they get refreshed.
To solve this you need to return from this.extractZipService.getJSON and map the results which will give you a collections of results and then you do forkJoin on results ( Not sure why you need to forkjoin as there are just the objects and not API's which you need to call )
this.logger.debug(`ConfigJson found for file '${element.name}': ${configJson}`);
const jsonContent = JSON.parse(configJson);
const entries = Object.entries(jsonContent);
entries.forEach((entry: any) => {
// code
});
complete code should look on similar lines :-
getAssetfilesData(assetIds: Array<string>, filter: RegExp, showConfig: boolean): Observable<AssetFilesTableItem[][]> {
const data = assetIds.map(assetId => {
// for each assetId
return this.fileService.getFileList(assetId)
.pipe(
map((datasets: any) => {
// iterate over each element
datasets.forEach((element: AssetFilesTableItem) => {
return this.extractZipService.getJSONfromZip(assetId, element.name,
'config.json')
});
})).map((configJson: any) => {
// collect your results and return from here
// return result
});;
});
// return combined result of each assetId request
return forkJoin(data);
}
}
I have created a Stackblitz(https://stackblitz.com/edit/nested-subscribe-solution) which work along the same lines. You need to use concatMap and forkJoin for getting all the results.
Hope this helps.

Categories

Resources