action calling action not working. Bad programming or issue?
Hi, i am using the latest wasp v12 version and started with a fresh saas template running on ubuntu Desktop 22.
I tested the Fileupload page and it works. Now i started developing my own page and action. The page will trigger a action and the action downloads a (for now) hard-coded file that should be uploaded with the createFile action. Meaning 1 action calls a second action. (My backup action calls createFile action). The first part, getting the file works but the calling createFile fails.
I copied the code from FileUploadPage.tsx into my backup.ts to trigger the upload. I changed the import in my backup.ts to:
import { createFile } from 'wasp/server/operations';
When i run the following code with "wasp start" i get the error during "Building SDK": " Expceted 2 arguments, but got 1 " with const { uploadUrl } = await createFile({ fileType, name } );
.
If i add a second argument like: const { uploadUrl } = await createFile({ fileType, name } , name);
i get the error: " ERR_MODULE_NOT_FOUND( " This error rises during " validating environment variables ... " on the server.
The attached screenshots show the Error. The code shown is from my Backup action getting the file. The File Upload from the frontend still works as expected I am not sure if i am unable to make this action to action in the way i do it? I am not able to figure out why it's not working.
Any help would be greatly appreciated. And of course thank you for the otherwise great framework and template.29 Replies
There is a thread where I had the same issue. Tl;Dr import { createFile } from 'wasp/client/operations';
Thanks for the reply. The error changed to ' Err Unsupported dir import '. See screenshot.
I tried removing the File code section and the uploadfile action call. I replaced it with a different working function but its still not working and throughs the same error.
Any thoughts on this too?
Hey, @smilebasti, thanks for reaching out. And @mindreaderlupoDO, thanks for helping, we appreciate it!
@smilebasti, I have a feeling @miho knows what's happening here, so I'm tagging him.
If not, I'll help you debug it
FYI, when you want to include code in your messages, please enclose it with backticks (`) instead of quotes. It looks much nicer. You can even use three backtics followed by the name of the language to get syntax highlighting:
Read more about it here: https://gist.github.com/matthewzring/9f7bbfd102003963f9be7dbcf7d40e51
@smilebasti , let's figure this out!
So, the first error you get, that second argument is missing, is due to you calling an action from action.
Calling action from an action is still a bit of an awkward case in Wasp -> you can do that, but what you then need to do, is correctly provide it with this second argument, which is
context
. That context
is provided by Wasp then you call the action from client -> wasp defines it and then provides it to your action. But now, when you are calling it directly from another action, it is up to you do provide that second argument, which can be a bit involved / unexpected -> in the future we will provide some nicer ways to do that. So right now, you would have to construct that context
object on your own.
Good thing is, since you are calling it from your other backup
action, you can reuse its context
. So, you can do const { uploadUrl } = await createFile({ fileType, name }, context);
. The only thing important for this to work is that your backup
action needs to declare, in main.wasp
file, that it uses File
entity, because createFile
action expects that entity in its context
. If you did that, you can forward that context
from backup
to createFile
.
Give that a try and let me know how it goes!
That said, I will immediately address two more things:
1. There is another way to share logic between actions -> instead of an action A1 calling action A2, you can extract logic from A2 into function F and then have both A1 and A2 use function F (in which case A1 doesn't use A2).
This means that you would create function createFileInTheDb(user, File, { fileType, name }) { ... }
that does most of the work in A2, and then both A1 and A2 can call that function.
That said, there is no real advantage in doing this over calling A2 from A1, but I wanted to offer it as an alternative in case you get heavily stuck with calling A2 from A1, maybe you can try this then.
2. You tried passing second argument to createFile
, which was a good direction, the problem is you gave it name
, instead of context
. However, the error you then got was a bit weird, mentioning query.js.js
-> that looks a bit suspicious to me, and I wonder if that is another error, orthogonal to the problem or wrong second argument. If this problem happens even after you provide context
as a second argument, then I would look into it on its own, more closely.
Let me know if passing context
works! If not, please also share the JS imports you have in your code, for the file where you implemented backup
action, those are quite important in this case.Thank you for the extensive reply. Very appreciated 🙂
I tried it with
const { uploadUrl } = await createFile({ fileType, name }, context);
but no luck: see Image.
My import in he backup action is
In the actions.js i call import { getLastImapMail } from './mail-backup/imap-backup.js';
and the action looks like this:
The action in the main.wasp has this action:
And the Page that calls the action with a button press looks like this:
I hope this information helps a little more...Yup it certainly does!
OK, I think we got this, we just need to figure out a couple more things.
Your "backup" action -> what is the name of that action? Is that
createLastMailMessage
? If so, I don't see you calling createFile
from there.
Can you also share its code? So that we then have all the code in question, and we can paint the whole picture.
Btw when providing code snippets, you can do put "js" after the three backticks, that will color the code correctly, make it easier to read.
What I would suggest doing is importing createFile
direclty with sometihng like import { createFile } from '../backup.ts'
or whatever is correct path for you, instead of doing import { createFile } from 'wasp/client/operations';
, that should make things simpler.
We did some changes recenlty here so I am not sure if import { createFile } from 'wasp/client/operations';
works as expected even though we mention it in docs, I will go explore that now
@miho @Filip , we have a user here wanting to call an Action from another Action.
Now, one way to go about it is importing its JS function directly, ignoring really that it is an action, by doing something like import { secondAction } from '../foo.js'
, and then they have to make sure to pass it correct context
.
On the other hand, our docs say that importing actions from client or server is the same, and that it is done with import { createFile } from 'wasp/client/operations'
, which looks quite suspicious to me, how would that work on server? Did we make a mistake here? If so, is it in the code/API, or in the docs? I am talking about this piece of docs: https://wasp-lang.dev/docs/data-model/operations/actions#using-actions .
We should figure this out, and then update our docs/API so that it is clear how to call an action from another action on the server.On the other hand, our docs say that importing actions from client or server is the same, and that it is done with import { createFile } from 'wasp/client/operations', which looks quite suspicious to me, how would that work on server? Did we make a mistake here? If so, is it in the code/API, or in the docs? I am talking about this piece of docs: https://wasp-lang.dev/docs/data-This is definitely a mistake. I was sure we changed that documentation with 0.12.0 But yes, it looks like we forgot. Definitely a mistake in the docs
@Filip ok! So, you shouldn't import from
wasp/client/operations
from the server, right? What should you do then, though? We can do "raw" importing as I described above, which is not terrible -> is there a better way though? Maybe we can import from wasp/server/operations
, does that exist?Maybe we can import from wasp/server/operations
, does that exist?
It does exist, yes, but still requires context (albeit a shroter one).
When calling an action/query imported from wasp/server/operations
, you need to pass a context that contains the user info (without the entities). If your query doesn't require auth, you should be able to pass an empty object.
Unfortunately, our type support for this is very lacking and we just display the function as (args: any, context: any) => Promise<any>
.
Still, calling it like this should work (assuming createFile
doesn't require auth):
If you need the user inside the query @smilebasti, let me know and I'll dig a little deeper@Filip actually if they are calling action A2 from action A1, it should be very easy to get user, they can just get it from the
context
of the A1.
@Filip you said that they don't need to pass entities in the context -> how is that? The context gets enriched with entities after the call? Remind me, we shaped this API like this on purpose -> was this that change you were doing at the very end of 0.12 release? How are we with docs in regards to this?The docs - non-existent, as w concluded above 😄
The API change - I think it's always been like this, but I'll investigate
Here's an issue for the docs change https://github.com/wasp-lang/wasp/issues/1909
GitHub
Fix docs on calling queries and actions on the server · Issue #1909...
Currently we say To use a Query, you can import it from wasp/client/operations and call it directly. As mentioned, the usage doesn't change depending on whether you're on the server or the ...
Oh sweet thanks @miho !
I added "shouldfix" label on this, I am using this to mark stuff that it is eager to be fixed
@smilebasti I hope this convo helps, and I am sorry for our omisions here regarding the docs! We recently updated them, with 0.12, and must have made some mistakes here. This should allow you to fix the issue -> but let us know if you will need any more help!
Hi guys, thank you very much for the replies.
I tried
but got the same error: Err_module_not_found.
but got the error: cannot find name 'context'.
Wohooo @smilebasti, you just became a Waspeteer level 1!
Can you give us the entire file?
but got the error: cannot find name 'context'.To me, this looks like a generic JavaScript reference error, not specific too Wasp But I'd have to see the file to say something useful
I then tried
but got the error: picture. Which looks more promising.
Haha you answerd to quick. One sec...
The backup.ts file:
I removed my logic. I test every iteration with my client side image as file and with the file that gets downloaded.
All my code is built upon the opensaas template. I only added on top and did not modify any of the already existing content. I want the same logic as the existing client-side file upload on the server-side with a file that the server pulls from the internet or generates.
Ok, I can try and replicate and see why this doesn't work later today. In the meantime, can you send me the variant of the code when you try this:
That one will probably be easier to fix
Error during 'building sdk'
No no, not the error, the code 😄
So from
createLastMailMessage
action you are calling getLastImapMail
. What you need to do is modify getLastImapMail
so it takes context
as an argument, then pass that context from createLastMailMessage
to getLastImapMail
and then finally to createFile
, as a second argument.
So:
In actions.js
:
And in backup.ts
:
@Filip I am a bit surprised that import { createFile } from 'wasp/server/operations'
didn't work for them thoughYes, that is indeed most curious
How sure are we at all that this works, in general? I haven't seen it in action as far as I can remember.
In general, pretty sure. We've tested it when we worked on the new SDK API and I have also been using it during my current task.
For example, this is the behavior I have on main in
waspc/examples/todo-typescript
:You are really trying to impress me with these visual effects, and you are succeeding :D.
Ok this is reassurring.
I wonder if possiblye the fact that operation is importing operation could play a role here (you are importing it from serverSetup, not another operation), although I don't see how.
No problem there either
You are really trying to impress me with these visual effects, and you are succeeding :D.This time I did it on purpose, but that's all I got