When using NestJS REPL I'm getting the following error despite running against an async function that I can see is returning a Promise.
Uncaught SyntaxError: await is only valid in async function
See the block below for the Service code I'm calling and an example of the code returning a promise but then throwing the error when trying to await.
CoffeesService.ts
async findOne(id: string) {
const coffee = await this.coffeeRepository.findOne(id, {
relations: ['flavors'],
});
if (!coffee) {
throw new NotFoundException(`Coffee ${id} not found`);
}
return coffee;
}
REPL
> coffeesService.findOne(1)
Promise {
<pending>,
[Symbol(async_id_symbol)]: 393,
[Symbol(trigger_async_id_symbol)]: 45,
[Symbol(destroyed)]: { destroyed: false }
}
> await coffeesService.findOne(1)
await coffeesService.findOne(1)
^^^^^
Uncaught SyntaxError: await is only valid in async function
According to NestJS docs, await is a top level function https://docs.nestjs.com/recipes/repl#:~:text=Native%20functions-,Read%2DEval%2DPrint%2DLoop%20(REPL),%7D%0A%3E%20await%20appController.getHello()%0A%27Hello%20World!%27,-To%20display%20all
This is a great new feature and I'm hoping I'm just doing something wrong :/
As suggested above, I needed to upgrade past node version 16. it works perfectly in node 18
> await get(CoffeesService).findOne(1)
Coffee {
id: 1,
name: 'Hobo roast',
brand: 'SF Roasters',
description: null,
recommendations: 0,
flavors: [
Flavor { id: 1, name: 'trash' },
Flavor { id: 2, name: 'cigarettes' }
]
}
Related
I am testing an rtk query mutation api call. I'm trying to match the mock result to a successful api call but I always get an error: 'Expected signal to be an instanceof AbortSignal'. I understand that this may be a side effect of jest-fetch-mock but am not sure how to convert the input(?) into a typeof AbortSignal.
test('successful response', async () => {
const wrapper = ({children}) => {
const storeRef = setupApiStore(testApi);
return <Provider store={storeRef.store}>{children}</Provider>;
};
const classState = {
statusCodes: 200,
message: 'Success',
details: {
overallPresent: 3,
overallTotal: 10,
classStrength: [
{className: 'class 1', present: 3, total: 3},
{className: 'class 2', present: 0, total: 7},
],
},
};
const body = {
schoolName: 'school 1',
className: 'class 1',
};
fetchMock.mockResponse(JSON.stringify(classState));
const {result, waitForNextUpdate} = renderHook(
() => useClassMutation(),
{wrapper},
);
const [students, initialResponse] = result.current;
expect(initialResponse.data).toBeUndefined();
expect(initialResponse.isLoading).toBe(false);
act(() => {
students(body);
});
await waitForNextUpdate({timeout: 500});
const loadedResponse = result.current;
console.log('loadedResponse: ', loadedResponse);
expect(loadedResponse).toEqual(paradeState); //error: 'Expected signal to be an instanceof AbortSignal'
});
This is not an RTK Query error - it's an error from the fetch polyfill you are using.
RTK Query is passing an AbortSignal into that fetch call - and then that fetch polyfill complains that AbortSignal is something incompatible.
=> if you polyfill global.fetch, you also have to polyfill global.AbortController with a compatible implementation.
How you do that is very different for each implementation of fetch, so you'll have to research that - but I hope this gives you a direction.
I am making a /ping command in my bot, following the discordjs guide, but when I use the command, I get an error:
TypeError: interaction.reply is not a function
at Object.<anonymous> (C:\Users\timda\code\discord bot\bot-data\commands\ping.ts:8:15)
at Generator.next (<anonymous>)
at C:\Users\timda\code\discord bot\bot-data\commands\ping.ts:8:71
at new Promise (<anonymous>)
at __awaiter (C:\Users\timda\code\discord bot\bot-data\commands\ping.ts:4:12)
at Object.execute (C:\Users\timda\code\discord bot\bot-data\commands\ping.ts:18:16)
at C:\Users\timda\code\discord bot\main.ts:43:18
at Generator.next (<anonymous>)
at C:\Users\timda\code\discord bot\main.ts:8:71
at new Promise (<anonymous>)
This is the /ping commands code:
import { SlashCommandBuilder } from 'discord.js';
module.exports = {
data: new SlashCommandBuilder()
.setName('ping')
.setDescription('Replies with Pong!'),
async execute(interaction: { reply: (arg0: string) => any }) {
console.log(interaction);
interaction.reply('Pong!');
},
};
And here is the code in my main file that loads in the SlashCommand files:
import fs from 'node:fs';
import path from 'node:path';
import { Client, Collection, GatewayIntentBits, Partials, REST, Routes } from 'discord.js';
import { clientId, token } from './config.json';
const client = new Client({
intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages, GatewayIntentBits.DirectMessages, GatewayIntentBits.MessageContent],
partials: [Partials.Message, Partials.Channel],
presence: { status: 'online', activities: [{ name: 'you 👀', type: 3 }] }
});
(client as any).commands = new Collection();
const commands: any[] = [];
for (const file of (fs.readdirSync(path.join('./bot-data/commands')).filter(file => file.endsWith('.ts')))) {
const command = require(`./bot-data/commands/${file}`);
(client as any).commands.set(command.data.name, command);
commands.push(command.data.toJSON());
};
client.on('ready', async (client: { user: { tag: any; }; }) => {
const rest = new REST({ version: '10' }).setToken(token);
try {
await rest.put(Routes.applicationCommands(clientId), { body: commands});
} catch (error) {
console.error(error);
}
console.log(`Succesfully logged in as ${client.user.tag}!`);
});
client.on('interactionCreate', async interaction => {
if(interaction.isCommand()) {
const command = (client as any).commands.get(interaction.commandName);
if (!command) {
await interaction.reply({content: `Something went wrong while executing ${interaction.commandName}.`, ephemeral: true});
return;
}
try {
await command.execute(client, interaction);
} catch (error) {
await interaction.reply({content: `Something went wrong while executing ${interaction.commandName}., ephemeral: true});
console.error(error);
}
}
});
The command does get loaded when /ping is used, because it does log the interaction in the console.
I am writing the bot in Typescript, that's why I did interaction: { reply: (arg0: string) => any }
and I'm using node to compile and run it.
NVM: version 1.1.10
NPM: version 8.19.2
I've tried writing interaction: { reply: (arg0: string) => any } in different ways and I've also already done a lot of googling, but I can't find the problem I'm facing here.
If someone could help me I would appreciate it.
Inside of main.ts, you're calling the execute function with these arguments:
await command.execute(client, interaction);
But inside of ping.ts, you're only accepting these arguments:
async execute(interaction: { reply: (arg0: string) => any }) { // etc
You need to include both client and interaction as arguments in ping.ts to prevent the values being passed into the wrong variables.
// I'm fairly certain that these are the right imports
import { BaseInteraction, SlashCommandBuilder, Client} from 'discord.js';
...
async execute(client: Client, interaction: BaseInteraction) { // etc
I have a function in my Firebase Cloud Functions that needs to run a scraping task. While the function is scraping the public website I need to return to the consumer a progress feedback. (eg. "Now it's at 10%", "Now it's at 30%", etc...).
I tought using Observables but I get weird results because the scraping function is async but it should return immediately the observable.
This is the simplified code of my Firebase Functions Backend:
exports.scrapeFunction = functions.region('europe-west2').https.onCall((data) => {
const res$ = new BehaviorSubject<Response>({
completion: 0,
completionMsg: 'One moment...',
error: undefined,
user: undefined,
});
scrape(data.id, res$);
return res$.asObservable();
});
const scrape = async (id: number, res$: BehaviorSubject<Response>) => {
const browser = await puppeteer.launch({ headless: true });
const page = await browser.newPage();
res$.next({
completion: 10,
completionMsg: 'I\'m loading the browser...'
})
await page.goto('https://example.com');
res$.next({
completion: 20,
completionMsg: 'Page opened...'
})
/* Other scraping async functions... */
}
On my frontend, instead I've a code similar to this:
response$ = Observable<Response> | undefined;
ngOnInit(): void {
const scrapeFunction= getFunctions(undefined, 'europe-west2');
const verifyClient = httpsCallable(functions, 'scrapeFunction');
const { data } = await scrapeFunction({ id: 0001 });
console.log('Data is', data);
this.response$ = (data as any).source as Observable<Response>;
this.response$.subscribe((res) => console.log(res));
return;
}
Logging in the console I get:
Data is
{source: {...}}
source:
closed: false
currentObservers: null
hasError: false
isStopped: false
observers: []
thrownError: null
_value: {completion: 0, completionMsg: 'One moment...'}
[[Prototype]]: Object
[[Prototype]]: Object
I also get the following error because the function is not returning an Observable as I know, but an object with the first key "source".
ERROR Error: Uncaught (in promise): TypeError: _this.response$.subscribe is not a function
TypeError: _this.response$.subscribe is not a function
at dashboard.component.ts:35:29
at Generator.next (<anonymous>)
at asyncGeneratorStep (asyncToGenerator.js:3:1)
at _next (asyncToGenerator.js:25:1)
at ZoneDelegate.invoke (zone.js:372:1)
at Object.onInvoke (core.js:28692:1)
at ZoneDelegate.invoke (zone.js:371:1)
at Zone.run (zone.js:134:1)
at zone.js:1276:1
at ZoneDelegate.invokeTask (zone.js:406:1)
at resolvePromise (zone.js:1213:1)
at zone.js:1120:1
at asyncGeneratorStep (asyncToGenerator.js:6:1)
at _next (asyncToGenerator.js:25:1)
at ZoneDelegate.invoke (zone.js:372:1)
at Object.onInvoke (core.js:28692:1)
at ZoneDelegate.invoke (zone.js:371:1)
at Zone.run (zone.js:134:1)
at zone.js:1276:1
at ZoneDelegate.invokeTask (zone.js:406:1)
Can someone understand this behavior?
[UPDATE 1]
I edited my index.ts file in Firebase Functions to be like this:
const res$ = new BehaviorSubject<Response>({
completion: 0,
completionMsg: 'One moment...',
});
exports.verifyClient = functions.https.onCall((data) => {
scrapeCheck(data.id);
return verifyClientRes$.asObservable();
});
const scrapeCheck = async (id: number) => {
await scrape(id, res$);
res$.complete();
return;
};
/* The scrape function is actually on an external file */
const scrape = (/* ... */) => {
/* ... */
}
** But i still got the same error on the console.
[UPDATE 2]
I created a StackBlitz project. You can find it here:
https://stackblitz.com/edit/wherescrypto?file=functions/src/index.ts
The function that is causing this error is in "functions/src/index.ts" and it's called "verifyClient"
Reviewing some documentation that could help to fix the issue you have, I was wondering if you already considered trying it with a realtime database or firestore.
You'll have to push your updates somewhere, the client app can see them that way. This solution was mentioned in a comment in this stackoverflow question.
Putting scrape in setTimeout will do the trick here. so that scrape is invoked after observable is returned.
exports.scrapeFunction = functions.region('europe-west2').https.onCall((data) => {
const res$ = new BehaviorSubject<Response>({
completion: 0,
completionMsg: 'One moment...',
error: undefined,
user: undefined,
});
setTimeout(() => scrape(data.id, res$));
return res$.asObservable();
});
I'm testing for the existence of 3 Subcomponents inside a Component. The test passes, but this warning is shown:
(node:52604) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'getters' of undefined
(node:52604) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 2)
(node:52604) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
This is the code of the Test:
const localVue = createLocalVue();
localVue.use(TPlugin(url)); //EDIT: ADD PLUGIN
localVue.use(Vuex);
it('WIP Fixing. Check the 3 Sub-Components of ChatWindow', () => {
const onClose = jest.fn();
const onMinimize = jest.fn();
const store = new Vuex.Store({
state: {
title: "My Web Chat",
titleIconUrl: "",
visibility: "minimized",
showCloseButton: false
},
getters: {
title: (state) => state.title,
lastName: (state) => state.lastName,
state: (state) => state.visibility
}
})
const wrapper = shallowMount(ChatWindow, {
propsData: {
onClose,
onMinimize
},
store,
localVue
})
expect(wrapper.findComponent(Header).exists()).toBe(true)
expect(wrapper.findComponent(MessageList).exists()).toBe(true)
expect(wrapper.findComponent(UserInput).exists()).toBe(true)
});
There must be some mistake in the way I mount the component with Vuex, any ideas?
UPDATE, here is an extract from my index.js:
export const store = new Vuex.Store({
state: {
title: "My funky title",
url: DEFAULT_URL,
visibility: "minimized",
//more variables....
},
mutations: {
title(state, newTitle) {
state.title = newTitle
},
url(state, newUrl) {
state.url = newUrl
},
visibility(state, visibility) {
state.visibility = visibility
},
//more variables...
}
getters: {
title: state => state.title,
url: state => state.url,
visibility: visibility => state.visibility,
//more variables.....
state: state => {
return { 'visibility': state.visibility }
},
}
})
window['TWChat'] = {
initialize(element, twcProps) {
Vue.prototype.$store = store;
if (twcProps.title) {
store.commit('title',twcProps.title);
}
///more similar if/commit blocks...
// check required properties
if (!store.getters.url) {
// TODO: thow error if url is missing?
return
}
Vue.use(TPlugin(store.getters.title));
Vue.prototype.$extensionMethods = functionMap;
//TChat is the parent of ChatWindow, the componentent of the Test
above.
new Vue({
render: (h) => h(TChat, { props: { } }),
}).$mount(element);
}
}
index.ejs code:
<div id="t-chat"></div>
<script src="t-chat.js"></script> -->
<script>
window.onload = function () {
var element = document.getElementById('t-chat');
const someProps = {
'url': '<%= process.env.ENDPOINT_URL %>',
'title':'<%= process.env.HEADER_TITLE %>'
}
window.TChat.initialize(element, someProps);
}
</script>
I have parts of app (modules) that gonna be forbidden for certain people, so I wanna check that in before hook and send unauthorized response if its needed.
I'm successfully throwing error on backend, but on my frontend I still get successful response as if there was no error.
Here is how my code looks like:
1.Function that checks if app is forbidden for user that sent request:
function isAppForbidden(hook) {
let forbiddenApps = [];
hook.app.services.settings.find({
query: {
$limit: 1,
$sort: {
createdAt: -1
}
}
}).then(res => {
let array = hook.params.user.hiddenApps;
if(array.indexOf('qualitydocs') >= 0 || res.data[0].forbiddenApps.indexOf('qualitydocs') >= 0) {
hook.response = Promise.reject({error: '401 Unauthorized'});
//this part is important, the rest not so much
//what im expecting to do here is just to return unauthorized response
}
});
return hook;
}
But this for now just throws error on backend like:
"error: Unhandled Rejection at: Promise Promise {
{ error: { code: '401', message: 'Unauthorized' } } } code=401, message=Unauthorized"
And frontend still gets successful response (200 with requested data)
And I just call this function in before hooks:
before: {
all: [
authenticate('jwt'),
hook => includeBefore(hook),
hook => isAppForbidden(hook) //here, rest is not important
],
find: [],
get: [],
create: [(hook) => {
hook.data.authorId = hook.params.user.id;
}],
update: [],
patch: [],
remove: []
},
the response im expecting to get, looks something like this:
Found the solution... the key was to wrap the content of function in promise... so it now looks like this:
function isAppForbidden(hook) {
return new Promise((resolve, reject) => {
hook.app.services.settings.find({
query: {
$limit: 1,
$sort: {
createdAt: -1
}
}
}).then(res => {
if (hook.params.user.hiddenApps.indexOf('qualitydocs') >= 0 || res.data[0].forbiddenApps.indexOf('qualitydocs') >= 0) {
reject(new errors.NotAuthenticated());
} else {
resolve();
}
})
})
}
and it works as a charm