Download and save PDF using Deno - deno

Hello I want to download pdf files using deno.
I'm using
export default async function downloadPDF(uid: string): Promise<void> {
const res = await fetch(`${Deno.env.get('MY_PDF_URL')}/${uid}`);
const file = await Deno.open(`./pdfs/${uid}.pdf`, { create: true, write: true })
if (res?.body) {
for await(const chunk of res.body) {
await Deno.writeAll(file, chunk);
}
}
file.close();
}
The command I am using is deno run --allow-net --allow-read --allow-write --allow-env --allow-run main.ts. So I don't think I havent put the right permissions here.
However I get the error
error: Uncaught (in promise) PermissionDenied: Permission denied (os error 13)
api_1 | const file = await Deno.open(`./pdfs/${uid}.pdf`, { create: true, write: true })
api_1 | ^
api_1 | at unwrapOpResult (deno:core/core.js:100:13)
api_1 | at async Object.open (deno:runtime/js/40_files.js:46:17)
api_1 | at async downloadPDF (file:///app/downloadPDF.ts:3:17)
What might be the issue here? I am using the Dockerfile
FROM hayd/alpine-deno:1.9.0
EXPOSE 1993
WORKDIR /app
USER deno
COPY deps.ts .
RUN deno cache deps.ts
COPY . .
RUN deno cache main.ts
CMD [run --allow-net --allow-read --allow-write --allow-env --allow-run main.ts]
to run this application.

import { readerFromStreamReader } from "https://deno.land/std#0.93.0/io/mod.ts";
export default async function downloadPDF(uid: string): Promise<void> {
const res = await fetch(`https://example.com/pdfs/${uid}`);
if (res.status !== 200) {
console.error(`response status was ${res.status}, this is not handled.`);
return;
}
// we don't want to download existing files
if (await fileExists(`/html/${uid}.html`)) {
console.info(`${uid}.html already exists, skipping...`);
return;
}
const file = await Deno.open(`/pdfs/${uid}.pdf`, { create: true, write: true, read: true })
if (res?.body) {
const reader = readerFromStreamReader(res.body.getReader());
await Deno.copy(reader, file);
}
file.close();
}
The error I posted in the question happens because of file permissions of the deno user in my Dockerfile. Simply removing the user solves the problem. However setting permissions correctly would be better if this is used for production.

Related

Cannot find module 'firebase-functions/lib/encoder' from 'node_modules/firebase-functions-test/lib/providers/firestore.js'

Hey I am trying to unit test this cloud function right here:
import { logger, region, https } from "firebase-functions/v1";
import Message from "../types/message";
const helloWorldHandler = region("europe-west1").https.onCall((_, context) => {
if (context.app == undefined) {
throw new https.HttpsError("failed-precondition", "The function must be called from an App Check verified app.");
}
logger.info("Hello logs!", { structuredData: true });
const message: Message = {
text: "Hello from Firebase!",
code: 200,
};
return message;
});
export default helloWorldHandler;
with the following test:
import * as functions from "firebase-functions-test";
import * as path from "path";
const projectConfig = {
projectId: "myproject-id",
};
const testEnv = functions(projectConfig, path.resolve("./flowus-app-dev-fb-admin-sdk-key"));
// has to be after initializing functions
import helloWorldHandler from "../src/functions/helloworld";
import Message from "../src/types/message";
describe('Testing "helloWorld"', () => {
const helloWorld = testEnv.wrap(helloWorldHandler);
it("helloWorld does work", async () => {
const data = {};
const success: Message = await helloWorld(data);
expect(success.code).toBe(200);
});
});
When I run it with yarn test I receive the following error
Cannot find module 'firebase-functions/lib/encoder' from 'node_modules/firebase-functions-test/lib/providers/firestore.js'
Even though my function does not even use firestore in the first place?
Any ideas ?
I was facing a similar issue while trying to set up a unit testing environment for firebase cloud functions.
Mainly, after following all the steps on Firebase's docs, and running npm test
I would get the following error
Error [ERR_PACKAGE_PATH_NOT_EXPORTED] Package subpath './lib/encoder'
is not defined by "exports"
After stumbling on Farid's suggestion for this problem, I realized that, for some reason, npm i firebase-functions-test does not install the latest version of the module.
Try npm i firebase-functions-test#latest.

Error: Functions did not deploy properly (delete callable function)

I am trying to deploy a delete function into the firebase cloud functions but then I get this error Error: Functions did not deploy properly. I have installed all the dependences e.g firebase-tools. Some of the steps I went through setting up the project are in these:
This is the code I am trying to deploy:
const admin = require('firebase-admin');
const firebase_tools = require('firebase-tools');
const functions = require('firebase-functions');
admin.initializeApp();
exports.mintAdminToken = functions.https.onCall(async (data, context) => {
const uid = data.uid;
const token = await admin
.auth()
.createCustomToken(uid, { admin: true });
return { token };
});
exports.recursiveDelete = functions
.runWith({
timeoutSeconds: 540,
memory: '2GB'
})
.https.onCall(async (data, context) => {
// Only allow admin users to execute this function.
if (!(context.auth && context.auth.token && context.auth.token.admin)) {
throw new functions.https.HttpsError(
'permission-denied',
'Must be an administrative user to initiate delete.'
);
}
const path = data.path;
console.log(
`User ${context.auth.uid} has requested to delete path ${path}`
);
// Run a recursive delete on the given document or collection path.
// The 'token' must be set in the functions config, and can be generated
// at the command line by running 'firebase login:ci'.
await firebase_tools.firestore
.delete(path, {
project: process.env.GCLOUD_PROJECT,
recursive: true,
yes: true,
token: functions.config().fb.token
});
return {
path: path
};
});
As from the comments, running the following solved the problem
npm install firebase-functions#latest firebase-admin#latest --save
npm install -g firebase-tools

Create a user programatically using Firebase Auth emulator

I am trying to write jest tests using the Firebase Auth emulator and continue to receive the following CORS error.
console.error
Error: Headers X-Client-Version forbidden
at dispatchError (/Users/me/my-project/node_modules/jsdom/lib/jsdom/living/xhr/xhr-utils.js:62:19)
at validCORSPreflightHeaders (/Users/me/my-project/node_modules/jsdom/lib/jsdom/living/xhr/xhr-utils.js:99:5)
at Request.<anonymous> (/Users/me/my-project/node_modules/jsdom/lib/jsdom/living/xhr/xhr-utils.js:367:12)
at Request.emit (events.js:315:20)
at Request.onRequestResponse (/Users/me/my-project/node_modules/request/request.js:1059:10)
at ClientRequest.emit (events.js:315:20)
at HTTPParser.parserOnIncomingClient [as onIncoming] (_http_client.js:641:27)
at HTTPParser.parserOnHeadersComplete (_http_common.js:126:17)
at Socket.socketOnData (_http_client.js:509:22)
at Socket.emit (events.js:315:20) undefined
The test is very simple:
import { renderHook, act } from "#testing-library/react-hooks"
import faker from "faker"
import { useAuth, FirebaseProvider, firebase } from "./index"
const wrapper = ({ firebase, children }) => {
return <FirebaseProvider firebase={firebase}>{children}</FirebaseProvider>
}
const createUser = ({ email = faker.internet.email(), password = faker.internet.password({ length: 6 }) } = {}) => {
return firebase
.auth()
.createUserWithEmailAndPassword(email, password)
.then(user => user)
}
const signUserIn = ({ email, password } = {}) => {
return firebase
.auth()
.signInWithEmailAndPassword(email, password)
.then(user => user)
}
describe("useAuth", () => {
it("will return the user", async () => {
const { result } = renderHook(() => useAuth(), { wrapper, initialProps: { firebase } })
const email = faker.internet.email()
const password = faker.internet.password()
await act(async () => {
const user = await createUser({ email, password }) // this fails
await signUserIn({ email, password }) //and so does this
})
expect(result.user).toEqual({ email, password })
})
})
And for reference, the index file:
const FirebaseProvider = ({ children, firebase }) => {
const firestore = firebase.firestore()
const auth = firebase.auth()
if (useEmulator()) {
firestore.useEmulator("localhost", 8080)
auth.useEmulator("http://localhost:9099/")
}
const value = { firestore, auth }
return <FirebaseContext.Provider value={value}>{children}</FirebaseContext.Provider>
}
const throwError = hook => {
throw new Error(`${hook} must be used within a FirebaseProvider`)
}
const useAuth = () => {
const context = useContext(FirebaseContext)
if (context === undefined) throwError("useAuth")
const [user, setUser] = useState()
useEffect(() => {
const cleanup = context.auth.onAuthStateChanged(authUser => {
authUser ? setUser(authUser) : setUser(null)
})
return () => cleanup()
})
return { ...context.auth, user }
}
I have tried using the REST endpoint that the actual emulator uses (below) and it errors in the same way.
http://localhost:9099/identitytoolkit.googleapis.com/v1/projects/<my-project>/accounts
Is there anyway to get this to run when using jest? Or do I need to create the accounts using the emulator UI, export them and re-import when I am running tests?
I have found I can use the REST endpoint below to make a user in the test, however it bypasses the emulator and makes a real user.
https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=<api-key>
Update jsdom version 16.5.2
This new version now supports wildcards for access-control-allow-headers, so updating to this version or using it as resolution, for projects created with Create React App, solves the problem.
Solution for jsdom prior to version 16.5.2
The error is thrown by jsdom because it doesn't support wildcard for access-control-allow-headers, but firebase uses the wildcard (see this issue for jsdom and this pull request related to firebase). There are two open pull requests to fix this issue: https://github.com/jsdom/jsdom/pull/3073 and https://github.com/jsdom/jsdom/pull/2867.
The issue can be fixed by either changing the relevant code manually in the node_modules folder or by using the fork as dependency in the package.json:
"jsdom": "silviot/jsdom#fix/allow-headers"
If jsdom isn't a direct dependency, then you can add the following to the package.json at the top level:
"resolutions": {
"jsdom": "silviot/jsdom#fix/allow-headers"
}
If the fork is used there are some auto-generated files missing in the jsdom folder. These can be generated by running npm install or yarn install in the folder. To automate this you can add a prepare script to the package.json:
"scripts": {
"prepare": "cd node_modules/jsdom && yarn"
},
I also had problems making users programaticly in the firebase auth emulator.
Instead of using
https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=[API_KEY]
You have to use the following format:
http://localhost:9099/identitytoolkit.googleapis.com/v1/accounts:signUp?key=[API_KEY]
Then giving a JSON body like this, and hit post
{
"email": "test#test.com",
"password": "test12"
}
And voila! You have a user in your emulator. Combine this with fetch or axios and you seed your emulator with users. If you need to add custom claims or other info, create function in the functions emulator that triggers on user creation.
functions.auth.user().onCreate

GatsbyJS with Firebase - WebpackError: ReferenceError: IDBIndex is not defined

I'm received error with gatsby develop. It's very similar to this one: https://github.com/firebase/firebase-js-sdk/issues/2222, but I'm received error with gatsby develop, not with gatsby build. I did a lot of research but I can't find working solution.
At first I had a problem with gatsby build, like in this post: https://github.com/firebase/firebase-js-sdk/issues/2222, but I resolved it with custom onCreateWebpackConfig(you can find it below).
Stack:
- Gatsby
- Firebase(error probably with that)
- Redux
I'm also delete .cache and node_modules and install everything again, but it didn't work.
Error:
There was an error compiling the html.js component for the development server.
See our docs page on debugging HTML builds for help https://gatsby.dev/debug-html ReferenceError: IDBIndex is not defined
]);
86 |
> 87 | proxyRequestMethods(Index, '_index', IDBIndex, [
| ^
88 | 'get',
89 | 'getKey',
90 | 'getAll',
WebpackError: ReferenceError: IDBIndex is not defined
- idb.mjs:87 Module../node_modules/idb/lib/idb.mjs
node_modules/idb/lib/idb.mjs:87:1
- index.esm.js:1 Module../node_modules/#firebase/installations/dist/index.esm.js
node_modules/#firebase/installations/dist/index.esm.js:1:1
- index.esm.js:1 Module../node_modules/#firebase/analytics/dist/index.esm.js
node_modules/#firebase/analytics/dist/index.esm.js:1:1
- index.esm.js:1 Module../node_modules/firebase/analytics/dist/index.esm.js
node_modules/firebase/analytics/dist/index.esm.js:1:1
- index.ts:1 Module../src/firebase/index.ts
src/firebase/index.ts:1:1
- index.esm.js:32 emit
node_modules/#firebase/analytics/dist/index.esm.js:32:1
My gatsby-node file:
exports.onCreateWebpackConfig = ({ stage, actions, getConfig }) => {
if (stage === 'build-html') {
actions.setWebpackConfig({
externals: getConfig().externals.concat(function(context, request, callback) {
const regex = /^#?firebase(\/(.+))?/;
if (regex.test(request)) {
return callback(null, `umd ${request}`);
}
callback();
}),
});
}
};
My firebase dependencies:
"#firebase/firestore-types": "^1.10.1",
"firebase": "^7.13.1",
"firebase-admin": "^8.10.0",
"firebase-functions": "^3.5.0",
"firebase-tools": "^7.16.1",
Firebase index file:
import firebase from 'firebase/app';
import 'firebase/firestore';
import 'firebase/auth';
import 'firebase/storage';
import 'firebase/analytics';
const firebaseConfig = {...};
firebase.initializeApp(firebaseConfig);
export const firestore = firebase.firestore();
export const auth = firebase.auth();
export const storage = firebase.storage();
Project repo: https://github.com/olafsulich/Projecty
Post on Github issues: https://github.com/firebase/firebase-js-sdk/issues/2946
Thanks in advance.
The following snippet will only work on build environment because of your condition (stage === 'build-html'):
exports.onCreateWebpackConfig = ({ stage, actions, getConfig }) => {
if (stage === 'build-html') {
actions.setWebpackConfig({
externals: getConfig().externals.concat(function(context, request, callback) {
const regex = /^#?firebase(\/(.+))?/;
if (regex.test(request)) {
return callback(null, `umd ${request}`);
}
callback();
}),
});
}
};
Remove it and use it like this:
exports.onCreateWebpackConfig = ({ stage, actions, getConfig }) => {
actions.setWebpackConfig({
externals: getConfig().externals.concat(function(context, request, callback) {
const regex = /^#?firebase(\/(.+))?/;
if (regex.test(request)) {
return callback(null, `umd ${request}`);
}
callback();
}),
});
};
Thank's a lot! It's working only on gatbsy develop, but now when I
want to build project, I get an error - TypeError: Cannot read
property 'concat' of undefined. You know how to solve it?
Regarding the new issue, you can follow a workaround in this topic, This is a common error in third-party modules in Gatsby when they try to reach a DOM element (usually window) that is not already defined when the app builds. So, you need to wait until window is defined. You can achieve this in two ways:
Instancing your firebase with a condition like this:
import firebase from '#firebase/app';
import '#firebase/auth';
import '#firebase/firestore';
import '#firebase/functions';
const config = {
... firebase config here
};
let instance;
export default function getFirebase() {
if (typeof window !== 'undefined') {
if (instance) return instance;
instance = firebase.initializeApp(config);
return instance;
}
return null;
}
Note the if (typeof window !== 'undefined') statement
By ignoring firebase module in you webpack configuration like shows their docs. In your gatsby-node.js:
exports.onCreateWebpackConfig = ({ stage, loaders, actions }) => {
if (stage === "build-html") {
actions.setWebpackConfig({
module: {
rules: [
{
test: /bad-module/,
use: loaders.null(),
},
],
},
})
}
}
Replace bad module for firebase (or the package/folder name in node_modules). Leave the slashes since test is a regular expression rule
This snippet replaces your previous one that seems to throw an error in concat() function.
For those who wants to try the concat() resolution, this will be helpful too:
exports.onCreateWebpackConfig = ({ stage, actions, getConfig }) => {
if (stage === 'build-html') {
actions.setWebpackConfig({
externals: getConfig().externals.concat((context, request, callback) => {
const regex = /^#?firebase(\/(.+))?/
// exclude firebase products from being bundled, so they will be loaded using require() at runtime.
if (regex.test(request)) {
return callback(null, `commonjs ${request}`) // <- use commonjs!
}
callback()
}),
})
}
}
Solved this problem!!
I'm using "gatsby": "^3.10.2", "firebase": "9.0.0-beta.6".
firebase needs to be set externals as commonjs.
gatsby-node.js:
exports.onCreateWebpackConfig = ({ stage, actions, getConfig }) => {
if (stage === 'build-html') {
actions.setWebpackConfig({
externals: getConfig().externals.concat((context, request, callback) => {
const regex = /^#?firebase(\/(.+))?/
// exclude firebase products from being bundled, so they will be loaded using require() at runtime.
if (regex.test(request)) {
return callback(null, `commonjs ${request}`) // <- use commonjs!
}
callback()
}),
})
}
}
Please try this setting.

connecting to firestore emulator with #firebase/testing

I am trying to test a firebase app locally.
I am running the test with firebase emulators:exec --only firestore 'mocha -r ts-node/register src/**/*.spec.ts
In my spec, I import #firebase/testing and setup my app and followed the directions from
https://firebase.google.com/docs/rules/unit-tests
I have a FirebaseService which is a singleton wrapper for my methods into which I inject my firebase app.
In production, I'll inject the firebase, and it gets initialized in the FirebaseService in testing, I initialize outside of the service.
The wrapper is fairly simple
export const FirebaseService = (function(): FirebaseSrvc {
let firebase;
const fbServiceObj: FirebaseSrvc = {
getInstance: (firebaseConfig, firebaseCore, initialize) => {
firebase = firebaseCore;
if (initialize && firebase.apps.length === 0) {
firebase.initializeApp(firebaseConfig);
}
return fbServiceObj;
},
createActivity: async (title: string) => {
try {
const firebaseUid = firebase.auth().currentuser.uid;
const newActivity: ActivityProps = {
title,
created_at: 123445,
created_by: firebaseUid,
public: false,
available_to: [firebaseUid],
};
console.log(' before create', newActivity);
const createResponse = await firebase
.firestore()
.collection('activities')
.doc(stringToSafeId(title))
.set(newActivity);
console.log('create response', createResponse);
return true;
} catch (e) {
console.log('error creating activity', e);
}
},
getActivity: async (title: string): Promise<ActivityProps> => {
try {
const actResponse: DocumentReferenceTo<ActivityProps> = await firebase
.firestore()
.collection('activities')
.doc(stringToSafeId(title))
.get();
return actResponse as ActivityProps;
} catch (e) {
console.log('error getting activity from firebase', e);
}
},
};
return fbServiceObj;
})();
The test I am attempting to run is
import * as firebase from '#firebase/testing';
import { assert } from 'chai';
import 'mocha';
import * as appConfig from '../../app-dev.json';
import { FirebaseService } from '../services/FirebaseService';
firebase.initializeTestApp({ ...appConfig.expo.extra.firebase, auth: { uid: 'random', email: 'test#test.com' } });
describe('Activity', async () => {
const fb = FirebaseService.getInstance(appConfig.expo.extra.firebase, testApp, false);
const activityData = new Activity(fb);
beforeEach(async () => await firebase.clearFirestoreData({ projectId }));
it('should create a new activity', async () => {
await activityData.set('test-activity'); // this runs FirebaseService.createActivity
const findActivity = await activityData.get('test-activity'); // this run FirebaseService.getActivity
assert(findActivity.title === 'test-activity');
});
});
When I run the test I get an error
Your API key is invalid, please check you have copied it correctly.] {
code: 'auth/invalid-api-key',
message: 'Your API key is invalid, please check you have copied it correctly.'
}
I can confirm that the API key which is passed into firebase.initializeTestApp matches the Web API Key in my firebase console.
I have also downloaded the google-services.json from my firebase console and lists
{
"api_key": [
{ "current_key": different_from_web_key}
]
}
And I have replaced my existing key with this new key, I still get the same error.
I have also tried setting up initializeTestApp({ projectId }) which is how the example from firebase docs sets it up, and I receive the same result.
I am using the same project details to run a project locally in android studio, and I am able to authenticate and write to firestore, so the API key I am using does work, but it appears to have issues being used in the test app.
This usually doesn't have a specific way to solve it. It might be that even a new copy and paste of the API key to the parameters, might make it work and the error to disappear.
I would recommend you to take a look at the following posts from the Community, that have some possible fixes for the error that you are facing.
Firebase Error: auth/invalid-api-key, Your API key is invalid, please check you have copied it correctly
Invalid API Key supplied using Firebase
In addition to that, since Firebase has free support offers, I think you reaching out to the Firebase support would help you fix this quickly. You should be able to contact directly for free.
Let me know if the information helped you!

Resources