GraphQL Authentication with React and Apollo - javascript

I'm currently following this guide (https://www.youtube.com/watch?time_continue=1614&v=_kYUzNVyj-0) to implement authentication in my react, graphql and apollo application. In the guide they have this function:
async function context(headers, constants) {
console.log('calling context') // This never prints, so it's not being called.
const user = await getUser(headers['authorization'])
return {
headers,
user
}
}
Below that function I have my resolvers and mutation for the GraphQL. One of the queries is supoosed to get the current user and that looks like this:
export default {
Date: GraphQLDate,
Query: {
currentUser: (root, args, { user }) => {
console.log(user) // undefined
console.log('currentUser')
return hello
},
...
In the video they later access the values via object destructuring, or just importing the whole object in their arguments:
When I'm trying to access anything from the return object from the context function I get undefined. It's due to the fact that the function is never being called, and I don't know why. It's probably something dumb I'm missing here.
I made a Gist of the code that I want to get working if that might help my shining knight that will hopefully save my day.
https://gist.github.com/Martinnord/6d48132db9af1f9e7b41f1266444011c
I can add more context to my question if anyone like! Thanks so much for reading!

You need to export your context function for Launchpad to pick it up. So
export async function context(headers, constants) {
console.log('calling context') // This never prints, so it's not being called.
const user = await getUser(headers['authorization'])
return {
headers,
user
}
}
It's not fully clear IMO that launchpad is looking for that context function. This is a tricky topic as well since the context function isn't required to make things work it doesn't notify you that it isn't there.

Related

Please explain why my code can't replace redux-thunk

I have just finished reading redux fundamentals from the Redux doc. I now understand what happens underneath middleware functions and some of the other features of Redux. But I can't help questioning why you must use redux-thunk when processing async logic when you can just do:
const fetchSomething = async () => {
const { data } = await axios('someEndpoint..');
dispatch({ type: 'FETCH_SOME_DATA', payload: data.something });
}
and import this from components that need this logic. Somehow similar to action creators, though it doesn't return an action object.
EDIT: reviewing my question, I realized that fetchSomething function needs dispatch as parameter in order for it to be reused in different components.

How to call React component function from Electron main.js using contextBridge

As the title states, I would like to call a function defined in one of my React components from the Electron main.js file. I know how to do it the other way around, where for example the user clicks a button and it calls a function in main.js and then returns to the React component. I need it to be the opposite, where main.js calls the React function and then returns the result to main.
I cannot simply have all the functionality in main.js since I cannot pass DOM objects to main from React (it has resulted in the error "object cannot be cloned"). So I figure it shouldn't be too hard to just send the request to React, have the React side of my app do the stuff, and then return the resulting string back to main.
I have found a number of other similar posts here where various solutions are offered, but none specifically for this exact problem. #1 exposes ipcRenderer which is a bad security practice I can't afford. #2 and #3 only explain it for vanilla Javascript, not React components. And while #4 does deal with React, it does not deal with Electron, and it is designed for React classes (I'm using functional components).
My resulting attempt looks like this mess:
const [mounted, setMounted] = useState(false)
if(!mounted){
window.reactFunction = myFunction;
}
useEffect(() =>{
setMounted(true)
},[])
window.electron.receive("fromMain", async () => {
return myFunction();
});
async function myFunction()
{
return "result";
}
However, the function instead gets called during rendering and is not called by the contextBridge. Here is my preload.js:
contextBridge.exposeInMainWorld('electron', {
receive: async (channel, func) => {
let validChannels = ["fromMain"];
if (validChannels.includes(channel)) {
return await ipcRenderer.on("fromMain", (event, ...args) => func(...args));
}
}
});
For some reason the preload.js script is not executed. And here is main.js:
const result = window.webContents.send('fromMain');
I have also found this discussion where it is made clear the security issues associated with calling ipcRenderer directly and so I would like to use contextBridge (which I'm already using for calling main functions from React anyway). It's also where my preload.js code comes from. But that example doesn't use React either.
Is this even possible? Or am I supposed to go about this problem a different way?
Looks like I made a typing mistake on this line:
return await ipcRenderer.on("fromMain", (event, ...args) => func(...args));
In this line I'm returning the function, so the function itself is not getting called. Remove the "return" and it will work. "Await" doesn't seem to make a difference.
However, once you do call the function, it can't actually return anything. So you need to make another call from renderer back to main, like this:
window.electron.receive("fromMain", async () => {
const returnValue = await myFunction();
window.electron.sendReturnValue(returnValue);
});
async function myFunction()
{
return "result";
}
Unless there is a still better way of doing it, but this solves my issue for now, and hopefully helps others.

Can I use axios in this example?

I'm still new to web development so I'm really sorry if the answer is obvious or the information provided by me is not much, but I hope this is sufficient:
Can I make an axios.post request within this vue.js component?
<script>
module.exports = {
mounted() {
// game code
};
</script>
Let me explain the problem I'm having at this moment: I can't import axios by import axios from "axios";. I neither can use export default { ... }. Both result in the page not loading (giving me an TypeError: "exports" is read-only). However, I need to access a variable within the gamecode since I want to axios.post that said variable (game score plus some more json info) to my MongoDB database.
If I can't make the request from there, am I able to get a variable from that code in mounted() { // game code }; and pass it to another component (and post it from there)? I searched the Internet for many hours but nothing seems to work for me, so again, sorry if it seems like I'm just too lazy to search for answers.
I managed to use axios within the module.exports part of the code.
Normally I would use import axios from 'axios'; and use axios as a method within
export default {
...
methods: {
// here (axios method)
...
But the
module.exports = { mounted() {
// game code
};
part did not work if I used the export default code part. I'm almost sure there is a more elegant way to solve this whole problem, but since I'm new and still don't understand totally how Vue and axios works, I simply tried some possibilities to include axios there and one worked:
module.exports = { mounted() {
const axios = require("axios");
// game code
// and later, for example: axios.post(...)...
};
At first I thought it wouldn't work since my IDE did not recognize the axios methods, but it actually posted data to my DB, so it did work.

Is it safe to call sagaMiddleware.run multiple times?

I'm using redux and redux-saga in an application to manage state and asynchronous actions. In order to make my life easier, I wrote a class that acts essentially as a saga manager, with a method that "registers" a saga. This register method forks the new saga and combines it with all other registered sagas using redux-saga/effects/all:
class SagasManager {
public registerSaga = (saga: any) => {
this._sagas.push(fork(saga));
this._combined = all(this._sagas);
}
}
This class is then used by my store to get the _combined saga, supposedly after all sagas are registered:
const store = Redux.createStore(
reducer,
initialState,
compose(Redux.applyMiddleware(sagaMiddleware, otherMiddleware)),
);
sagaMiddleware.run(sagasManager.getSaga());
However, I ran into the problem that depending on circumstances (like import order), this doesn't always work as intended. What was happening was that some of the sagas weren't getting registered before the call to sagaMiddleware.run.
I worked around this by providing a callback on SagasManager:
class SagasManager {
public registerSaga = (saga: any) => {
this._sagas.push(fork(saga));
this._combined = all(this._sagas);
this.onSagaRegister();
}
}
And then the store code can use this as
sagasManager.onSagaRegister = () => sagaMiddleware.run(sagasManager.getSaga());
This seems to work, but I can't find in the docs whether this is safe. I did see that .run returns a Task, which has methods for canceling and the like, but since my problem is only in that awkward time between when the store is constructed and the application is rendered I don't that would be an issue.
Can anyone explain whether this is safe, and if not what a better solution would be?
It may depend on what you mean by "safe". What exactly do you mean by that in this case?
First, here's the source of runSaga itself, and where it gets used by the saga middleware.
Looking inside runSaga, I see:
export function runSaga(options, saga, ...args) {
const iterator = saga(...args)
// skip a bunch of code
const env = {
stdChannel: channel,
dispatch: wrapSagaDispatch(dispatch),
getState,
sagaMonitor,
logError,
onError,
finalizeRunEffect,
}
const task = proc(env, iterator, context, effectId, getMetaInfo(saga), null)
if (sagaMonitor) {
sagaMonitor.effectResolved(effectId, task)
}
return task
}
What I'm getting out of that is that nothing "destructive" will happen when you call runSaga(mySagaFunction). However, if you call runSaga() with the same saga function multiple times, it seems like you'll probably have multiple copies of that saga running, which could result in behavior your app doesn't want.
You may want to try experimenting with this. For example, what happens if you have a counter app, and do this?
function* doIncrement() {
yield take("DO_INCREMENT");
put({type : "INCREMENT"});
}
sagaMiddleware.runSaga(doIncrement);
sagaMiddleware.runSaga(doIncrement);
store.dispatch({type : "DO_INCREMENT"});
console.log(store.getState().counter);
// what's the value?
My guess is that the counter would be 2, because both copies of doIncrement would have responded.
If that sort of behavior is a concern, then you probably want to make sure that prior sagas are canceled.
I actually ran across a recipe for canceling sagas during hot-reloading a while back, and included a version of that in a gist for my own usage. You might want to refer to that for ideas.

Testing Complex Asynchronous Redux Actions

So, let's say I have the next action:
export function login({ email, password, redirectTo, doNotRedirect }) {
return ({ dispatch }) => {
const getPromise = async () => {
const basicToken = Base64.encode(`${email}:${password}`);
const authHeaders = { Authorization: `Basic ${basicToken}` };
const { payload, error } = await dispatch(sendAuthentication(authHeaders));
if (error) throw payload;
const { username, token, fromTemporaryPassword } = payload;
const encodedToken = Base64.encode(`${username}:${token}`);
dispatch(persistence.set('authorizationToken', encodedToken));
dispatch(postGlobalId({ username }));
dispatch(setIsLoggedIn(true));
dispatch(setIsFromTemporaryPassword(fromTemporaryPassword));
await dispatch(clientActions.fetchClient);
if (doNotRedirect) return;
if (fromTemporaryPassword)
dispatch(updatePath('/profile/change-password'));
else
dispatch(updatePath(redirectTo || '/dashboard'));
};
return {
type: AUTHENTICATION_LOGIN,
payload: getPromise()
};
};
}
And I want to add tests for it, to add reliability to the code.
So, here are few things:
We send authentication headers and get data as a response
We throw an error if some error is present in the response
We set up all needed tokens, dispatch all needed actions to show that we are logged in now
Fetching client data
Based on params and received data, we redirect to needed route / don't redirect
The question is that it is really too hard to test and we need to stub literally everything, which is bad due to brittle tests, fragility and too much of implementation knowing (not to mention that it is pretty challenging to stub dispatch to work properly).
Therefore, should I test all of these 5 points, or to focus only on the most important stuff, like sending authorization request, throw error and check redirects? I mean, the problem with all flags that they can be changed, so it is not that reliable.
Another solution is just to separate these activities into something like following:
auth
setLoginInfo
handleRedirects
And to pass all needed functions to invoke through dependency injection (here just with params, basically)? With this approach I can spy only invoking of this functions, without going into much details.
I am quite comfortable with unit testing of pure functions and handling different edge-cases for them (without testing too much implementation, just the result), but testing complex functions with side-effects is really hard for me.
If you have very complex actions like that, I think an alternative (better?) approach is to have simple synchronous actions instead (you can even just dispatch payloads directly, and drop action creators if you like, reducing boiler-plate), and handle the asynchronous side using redux-saga: https://github.com/yelouafi/redux-saga
Redux Saga makes it very simple to factor out your business logic code into multiple simple generator functions that can be tested in isolation. They can also be tested without the underlying API methods even being called, due to the 'call' function in that library: http://yelouafi.github.io/redux-saga/docs/api/index.html#callfn-args. Due to the use of generators, your test can 'feed' values to the saga using the standard iterator.next method. Finally, they make it much easier for reducers to have their say, since you can check something from store state (e.g. using a selector) to see what to do next in your saga.
If Redux + Redux Saga had existed before I started on my app (about 100,000 JS(X) LOC so far), I would definitely have used them.

Categories

Resources