I am using Jest to test my firebase functions. This is all in the browser, so I don't have any conflicts with firebase on the server side. When I use firebase.auth() or firebase.database() everything works fine. When I try to use firebase.storage() my tests fail.
Here is my firebase import and initialization:
import firebase from 'firebase';
import config from '../config';
export const firebaseApp = firebase.initializeApp(config.FIREBASE_CONFIG);
export const firebaseAuth = firebaseApp.auth();
export const firebaseDb = firebaseApp.database();
I have an imageUtils file that has an upload function in it:
import { firebaseApp } from './firebase';
export const uploadImage = (firebaseStoragePath, imageURL) => {
return new Promise((resolve, reject) => {
// reject if there is no imagePath provided
if (!firebaseStoragePath) reject('No image path was provided. Cannot upload the file.');
// reject if there is no imageURL provided
if (!imageURL) reject('No image url was provided. Cannot upload the file');
// create the reference
const imageRef = firebaseApp.storage().ref().child(firebaseStoragePath);
let uploadTask;
// check if this is a dataURL
if (isDataURL(imageURL)) {
// the image is a base64 image string
// create the upload task
uploadTask = imageRef.putString(imageURL);
} else {
// the image is a file
// create the upload task
uploadTask = imageRef.put(imageURL);
}
// monitor the upload process for state changes
const unsub = uploadTask.on(firebase.storage.TaskEvent.STATE_CHANGED,
(snapshot) => {
// this is where we can check on progress
}, (error) => {
reject(error.serverResponse);
unsub();
}, () => {
// success function
resolve(uploadTask.snapshot.downloadURL);
unsub();
});
});
};
And I am trying to create a test case for that function and every time it fails with:
TypeError: _firebase3.firebaseApp.storage is not a function
When I run the app normally everything works fine and I never get errors about storage() being undefined or not a function. It is only when I try to run a test case.
I have set a console.dir(firebaseApp); line in the firebase import, and it comes back with both auth() and database() but no storage. How can I get storage to import/initialize/exist properly?
Add the following import
import "firebase/storage";
It looks like this was fixed in a recent update to the firebase javascript package:
I had the same problem. Another possible solution:
import * as firebase from "firebase";
import "firebase/app";
import "firebase/storage";
Related
i am facing issue i am trying to get only 10 data from firebase but how can i achieve that if ref(database, "messages/").limitToFirst(10) is throwing:
ref(...).limitToFirst is not a function
My code:
const newMsg = ref(database, 'messages/').limitToFirst(10);
onChildAdded(newMsg, (data) => {
if(data.val().name != name){
var divData = '<div class="message"><div class="others">'+data.val().name+': </div><div>'+data.val().message+'</div></div>';
//oponent message
$("#chat-history").append(divData);
}else{
var divData = '<div class="message"><div class="user">'+data.val().name+': </div><div>'+data.val().message+'</div></div>';
//sent message
$("#chat-history").append(divData);
}
});
Where database is
const app = initializeApp(firebaseConfig);
const database = getDatabase(app);
And my imports look like this:
import {initializeApp} from "https://www.gstatic.com/firebasejs/9.6.6/firebase-app.js";
import {
getDatabase,
set,
ref,
push,
child,
onValue,
onChildAdded
} from "https://www.gstatic.com/firebasejs/9.6.6/firebase-database.js";
EDIT:
After a hour i see where is the problem in my import i need to add query, limitToLast
import {
getDatabase,
set,
ref,
query,
push,
child,
onValue,
onChildAdded,
limitToLast
} from "https://www.gstatic.com/firebasejs/9.6.6/firebase-database.js";
And in my function just call it like this:
const newMsg = query(ref(database, 'messages/'), limitToLast(10));
onChildAdded(newMsg, (data) => {
In the new Modular SDK (v9.0.0+), you can build a Query using query() function (instead of chaining the QueryConstraints):
import { ref, query, limitToFirst } from "firebase/database"
const newMsg = query(ref(database, 'messages/'), limitToFirst(10))
onChildAdded(newMsg, (data) => {
// ...
})
The new SDK uses a functional syntax unlike the older name-spaced one so limitToFirst is a top-level function now as in above code snippet.
Also checkout:
How to read, write and query data in Firebase Realtime Database using Firebase SDK v9 (Modular)
Working with lists of data
I have managed to use fleek to update IPFS via straight javascript. I am now trying to add this functionality to a clean install of a svelteKit app. I think I am having trouble with the syntax around imports, but am not sure what I am doing wrong. When I click the button on the index.svelte I get the following error
Uncaught ReferenceError: require is not defined
uploadIPFS upload.js:3
listen index.mjs:412..........(I truncated the error here)
A few thoughts
I am wondering if it could be working in javascript because it is being called in node (running on the server) but running on the client in svelte?
More Details
The index.svelte file looks like this
<script>
import {uploadIPFS} from '../IPFS/upload'
</script>
<button on:click={uploadIPFS}>
upload to ipfs
</button>
the upload.js file looks like this
export const uploadIPFS = () => {
const fleek = require('#fleekhq/fleek-storage-js');
const apiKey = 'cZsQh9XV5+6Nd1+Bou4OuA==';
const apiSecret = '';
const data = 'pauls test load';
const testFunctionUpload = async (data) => {
const date = new Date();
const timestamp = date.getTime();
const input = {
apiKey,
apiSecret,
key: `file-${timestamp}`,
data
};
try {
const result = await fleek.upload(input);
console.log(result);
} catch (e) {
console.log('error', e);
}
};
testFunctionUpload(data);
};
I have also tried using the other import syntax and when I do I get the following error
500
global is not defined....
import with the other syntax is
import fleekStorage from '#fleekhq/fleek-storage-js';
function uploadIPFS() {
console.log('fleekStorage',fleekStorage)
};
export default uploadIPFS;
*I erased the api secret in the code above. In future I will store these in a .env file.
Even more details (if you need them)
The file below will update IPFS and runs via the command
npm run upload
That file is below. For my version that I used in svelte I simplified the file by removing all the file management and just loading a variable instead of a file (as in the example below)
const fs = require('fs');
const path = require('path');
const fleek = require('#fleekhq/fleek-storage-js');
require('dotenv').config()
const apiKey = process.env.FLEEK_API_KEY;
const apiSecret = process.env.FLEEK_API_SECRET;
const testFunctionUpload = async (data) => {
const date = new Date();
const timestamp = date.getTime();
const input = {
apiKey,
apiSecret,
key: `file-${timestamp}`,
data,
};
try {
const result = await fleek.upload(input);
console.log(result);
} catch(e) {
console.log('error', e);
}
}
// File management not used a my svelte version to keep it simple
const filePath = path.join(__dirname, 'README.md');
fs.readFile(filePath, (err, data) => {
if(!err) {
testFunctionUpload(data);
}
})
I have on my site an page where I handled File upload, but since I upgraded Firebase (I guess it was Firebase v7/8) this particular feature is not working anymore.
To handled the file upload in firebase storage I created a custom hook, where I use the useEffect because I need it to run each time there is a new file value. I passed a parametter (file) for the file I'm trying to upload and store it in database, and that way databse contains all image's url. Then I used the datas to load images in a react components.
The error I've got:
Uncaught TypeError: projectStorage.ref is not a function
Since I'm on Firebase v9 I'm lillte bit confused about it, and don't know what to change. Thank you for your help, I really appreciate =).
useStorage.jsx (custom hook)
import {projectStorage, projectFirestore, timestamp} from '../Firebase'
import { useEffect, useState } from 'react'
function useStorage(file) {
const [progress, setProgress] = useState(0);
const [error, setError] = useState(null);
const [url, setUrl] = useState(null);
useEffect(() => {
const storageRef = projectStorage.ref(file.name)
const collectionRef = projectFirestore.collection('images');
storageRef.put(file).on('state_changed', (snap) => {
let percent = (snap.bytesTransferred / snap.totalBytes) * 100;
setProgress(percent >> 0); // or Math.trunc()
}, (err) => {
setError(err);
}, async () =>{
const url = await storageRef.getDownloadURL();
const createdAt = timestamp();
collectionRef.add({ url, createdAt});
setUrl(url);
});
}, [file]);
return {progress, url, error};
}
export default useStorage;
There's a top level function uploadBytesResumable() to upload files while monitoring progress. Try refactoring the code as shown below:
import {projectStorage, projectFirestore, timestamp} from '../Firebase'
import { ref as storageRef, uploadBytesResumable } from "firebase/storage";
useEffect(() => {
// Creating a storage reference
const storageReference = storageRef(projectStorage, file.name);
// Creating an upload task
const uploadTask = uploadBytesResumable(storageReference, file);
// Monitoring upload progress
uploadTask.on("state_changed", (snapshot: any) => {
console.log(snapshot);
// render progress
});
}, [file])
Checkout the documentation on Upload files with Cloud Storage on Web.
I am trying to upload file to s3, before that I am altering the name of the file. Now I am accepting 2 files from request form-data object, renaming the filename, and uploading the file to s3. And end of the task I need to return the renamed file list which is uploaded successfully.
I am using S3.upload() function. But the problem is, the variable which is assigned as empty array initially, that will contain the renamed file list. But the array is returning empty response. The s3.upload() is taking much time. is there any probable solution where I can store the file name if upload is successful and return those names in response.
Please help me to fix this. The code looks like this,
if (formObject.files.document && formObject.files.document.length > 0) {
const circleCode = formObject.fields.circleCode[0];
let collectedKeysFromAwsResponse = [];
formObject.files.document.forEach(e => {
const extractFileExtension = ".pdf";
if (_.has(FILE_EXTENSIONS_INCLUDED, _.lowerCase(extractFileExtension))) {
console.log(e);
//change the filename
const originalFileNameCleaned = "cleaning name logic";
const _id = mongoose.Types.ObjectId();
const s3FileName = "s3-filename-convension;
console.log(e.path, "", s3FileName);
const awsResponse = new File().uploadFileOnS3(e.path, s3FileName);
if(e.hasOwnProperty('ETag')) {
collectedKeysFromAwsResponse.push(awsResponse.key.split("/")[1])
}
}
});
};
use await s3.upload(params).promise(); is the solution.
Use the latest code - which is AWS SDK for JavaScript V3. Here is the code you should be using
// Import required AWS SDK clients and commands for Node.js.
import { PutObjectCommand } from "#aws-sdk/client-s3";
import { s3Client } from "./libs/s3Client.js"; // Helper function that creates Amazon S3 service client module.
import {path} from "path";
import {fs} from "fs";
const file = "OBJECT_PATH_AND_NAME"; // Path to and name of object. For example '../myFiles/index.js'.
const fileStream = fs.createReadStream(file);
// Set the parameters
export const uploadParams = {
Bucket: "BUCKET_NAME",
// Add the required 'Key' parameter using the 'path' module.
Key: path.basename(file),
// Add the required 'Body' parameter
Body: fileStream,
};
// Upload file to specified bucket.
export const run = async () => {
try {
const data = await s3Client.send(new PutObjectCommand(uploadParams));
console.log("Success", data);
return data; // For unit tests.
} catch (err) {
console.log("Error", err);
}
};
run();
More details can be found in the AWS JavaScript V3 DEV Guide.
Goal: To support dynamic loading of Javascript modules contingent on some security or defined user role requirement such that even if the name of the module is identified in dev tools, it cannot be successfully imported via the console.
A JavaScript module can be easily uploaded to a cloud storage service like Firebase (#AskFirebase) and the code can be conditionally retrieved using a Firebase Cloud Function firebase.functions().httpsCallable("ghost"); based on the presence of a custom claim or similar test.
export const ghost = functions.https.onCall(async (data, context) => {
if (! context.auth.token.restrictedAccess === true) {
throw new functions.https.HttpsError('failed-precondition', 'The function must be called while authenticated.');
}
const storage = new Storage();
const bucketName = 'bucket-name.appspot.com';
const srcFilename = 'RestrictedChunk.chunk.js';
// Downloads the file
const response = await storage
.bucket(bucketName)
.file(srcFilename).download();
const code = String.fromCharCode.apply(String, response[0])
return {source: code};
})
In the end, what I want to do...
...is take a webpack'ed React component, put it in the cloud, conditionally download it to the client after a server-side security check, and import() it into the user's client environment and render it.
Storing the Javascript in the cloud and conditionally downloading to the client are easy. Once I have the webpack'ed code in the client, I can use Function(downloadedRestrictedComponent) to add it to the user's environment much as one would use import('./RestrictedComponent') but what I can't figure out is how to get the default export from the component so I can actually render the thing.
import(pathToComponent) returns the loaded module, and as far as I know there is no option to pass import() a string or a stream, just a path to the module. And Function(downloadedComponent) will add the downloaded code into the client environment but I don't know how to access the module's export(s) to render the dynamically loaded React components.
Is there any way to dynamically import a Javascript module from a downloaded stream?
Edit to add: Thanks for the reply. Not familiar with the nuances of Blobs and URL.createObjectURL. Any idea why this would be not found?
const ghost = firebase.functions().httpsCallable("ghost");
const LoadableRestricted = Loadable({
// loader: () => import(/* webpackChunkName: "Restricted" */ "./Restricted"),
loader: async () => {
const ghostContents = await ghost();
console.log(ghostContents);
const myBlob = new Blob([ghostContents.data.source], {
type: "application/javascript"
});
console.log(myBlob);
const myURL = URL.createObjectURL(myBlob);
console.log(myURL);
return import(myURL);
},
render(loaded, props) {
console.log(loaded);
let Component = loaded.Restricted;
return <Component {...props} />;
},
loading: Loading,
delay: 2000
});
Read the contents of the module file/stream into a BLOB. The use URL.createObjectURL() to create your dynamic URL to the BLOB. Now use import as you suggested above:
import(myBlobURL).then(module=>{/*doSomethingWithModule*/});
You can try using React.lazy:
import React, {lazy, Suspense} from 'react';
const Example = () => {
const [userAuthenticated, setUserAuthenticated] = useState(true);
if (userAthenticated) {
const RestrictedComponent = lazy(() => import('./RestrictedComponent'));
return (
<div>
<Suspense fallback={<div><p>Loading...</p></div>}>
<RestrictedComponent />
</Suspense>
</div>
)
}
return (
<div>
<h1>404</h1>
<p>Restricted</p>
</div>
);
}
export default Example;