Query after mutation is done with VueJS 3 Composition API - vue.js

Im pretty new to vue and the whole topic but my goal is to run a myUser query after the jwt token is saved.
JWT saving is working but i have no clue how the proceed after the mutation is done.
Im thinking about a watch() on loggedIn ref but im not sure.
Tried several things, but no solution found and due the lack of information in the www im hoping for you help.
Here is my core logic
<script>
import gql from 'graphql-tag';
import { useMutation, useQuery } from '#vue/apollo-composable';
import { ref, computed, watch } from 'vue';
import { useUserStore } from '../stores/user';
import { myUser } from '#/apollo/queries';
import { loginUser } from '#/apollo/mutations';
export default {
name: 'LoginForm',
setup() {
let username = ref('');
let password = ref('');
let loggedIn = ref(false);
const error = computed(() => {
return username.value === '' ? 'The username is required' : '';
});
const {
mutate: loginUserMutation,
onDone,
onError,
} = useMutation(loginUser);
onDone((res) => {
if (
res?.data?.loginUser && res.data.loginUser.success
) {
localStorage.setItem('access-token', res.data.loginUser.msg);
useUserStore().$patch({
username: res.data.loginUser.msg,
});
loggedIn.value = true;
>>>>>>>>>>> Now with the JWT token, call myUser query <<<<<<<<<<
}
});
const loginBtnClicked = () => {
loginUserMutation({
username: username.value,
password: password.value,
platform: 'PC',
});
};
return {
username,
password,
error,
loginBtnClicked,
loginUserMutation,
};
},
};
</script>

how about...
create function const myUser() => { -> myuser query logic... }
after localStorage.setItem('access-token', res.data.loginUser.msg);
you call myUser function

Related

vuex unknown action type: login

Login.vue
<script setup>
import { useLayout } from '#/layout/composables/layout';
import { ref, computed } from 'vue';
import AppConfig from '#/layout/AppConfig.vue';
import { decodeCredential } from 'vue3-google-login'
import {auth} from '../../../store/modules/auth.module';
import { useStore } from "vuex";
const store = useStore()
const { layoutConfig, contextPath } = useLayout();
const email = ref('');
const password = ref('');
const checked = ref(false);
const logoUrl = computed(() => {
return `${contextPath}layout/images/${layoutConfig.darkTheme.value ? 'logo-white' : 'logo-dark'}.svg`;
});
const callback = (response) => {
const userData = decodeCredential(response.credential);
// const authStore = auth;
// console.log(authStore.login());
if (userData.email=='****#gmail.com') {
return store.dispatch('login')
}
}
</script>
auth.module.js
import AuthService from "../../services/auth.service";
const user = JSON.parse(localStorage.getItem('token'));
const initialState = user
? { status: { loggedIn: true }, user }
: { status: { loggedIn: false }, user: null };
export const auth = {
namespaced: true,
state: initialState,
actions: {
login({ commit }, user) {
return AuthService.login(user).then(
user => {
commit('loginSuccess', user);
return Promise.resolve(user);
},
error => {
commit('loginFailure');
return Promise.reject(error);
}
);
},
logout({ commit }) {
AuthService.logout();
commit('logout');
},
},
mutations: {
loginSuccess(state, user) {
state.status.loggedIn = true;
state.user = user;
},
loginFailure(state) {
state.status.loggedIn = false;
state.user = null;
},
logout(state) {
state.status.loggedIn = false;
state.user = null;
},
}
};
auth.service.js
import axios from 'axios';
const API_URL = 'http://localhostGetToken';
class AuthService {
async login(user) {
const response = await axios
.post(API_URL, {
username: user.username='admin',
password: user.password='password'
});
if (response.data.accessToken) {
localStorage.setItem('token', JSON.stringify(response.token));
}
console.log(response);
return response.data;
}
async logout() {
localStorage.removeItem('token');
}
}
export default new AuthService();
Here i trying to login if email true to trigger login vuex.but i get a error [vuex] unknown action type: login
how to solve this?
You haven't included in your question how the auth store is linked to your application.
I'm guessing you have a main store and the auth store is one of its modules.
If my guess is true, you should dispatch auth/login, not login, since the main store doesn't have a login action.
Side note: I suggest you carefully read How to Ask, to improve the quality of your future questions.
The problems with your current question:
you posted too much irrelevant code and, at the same time, you haven't posted all the relevant code. You should have included:
a) the action deemed unknown (everything else in that store is irrelevant for this question)
b) how the store is linked to the app (main store + how the store is instantiated in the app) - these bits are missing
c) how you're consuming the action in the component (everything else in the component is irrelevant for the question)
you started with the code. Always start by explaining the problem, so when people look at the code, they know what to look for (and skip the irrelevant parts). This is also helpful for future users with a similar problem: they'll be able to quickly understand if your question is relevant for their problem.
The more users find the question useful, the more chances for it to get upvoted.
Another side-note: the condition used to dispatch is, most likely, wrong. It is only true when the email is actually '****#gmail.com'.
You should probably use if (userData.email.endsWith('#gmail.com')).

Vue-Apollo V4 composition API #vue/apollo-composable on loginform. TS2349: This expression is not callable. Type 'Ref<any>' has no call signatures

I am a new one in the apollo world. I use Vue composition API and #vue/apollo-composable with version V4 for my vue apollo client. And the backend is nodejs server with apollo server.
Now I have a problem on the login page with useQuery, if I call the result of the query, they will be show the error
TS2349: This expression is not callable. Type 'Ref' has no call signatures
import { ref, defineComponent } from '#vue/composition-api'
import gql from 'graphql-tag'
import { useUserLogin } from '#/mixins/user-login'
import { CustomRules } from '#/validators/rules'
import { useQuery, useMutation } from '#vue/apollo-composable'
export default defineComponent({
props: {
msg: String,
},
setup(props, { root }) {
const form = ref()
const rules = ref(CustomRules)
const username = ref('')
const password = ref('')
const errorMessage = ref('')
const { result: loginBenutzer } = useQuery(gql`
query loginBenutzer($kuerzel: String!, $passwort: String!) {
loginBenutzer(kuerzel: $kuerzel, passwort: $passwort) {
user {
kuerzel: BEN_MIA_KUERZEL,
name: BEN_NAME
},
token
}
}
`)
function login() {
if (form.value.validate()) {
loginBenutzer({ kuerzel: username.value, passwort: password.value })
.then(data => {
useUserLogin(data.data.loginBenutzer)
root.$router.push('/hm')
})
.catch(err => {
errorMessage.value = err.message
console.log(err)
})
}
}
return {
form,
rules,
username,
password,
login,
errorMessage,
}
},
})
Calling to result: loginBenutzer of this line
loginBenutzer({ kuerzel: username.value, passwort: password.value })
the loginBenutzer shows the error:
TS2349: This expression is not callable. Type 'Ref' has no call signatures
And in the Apollo server type is defined like this
type Query {
loginBenutzer(kuerzel: String!, passwort: String!): LoginResponse!,
}
but if I change the query to mutation, then they are working. Like this
const { mutate: loginBenutzer } = useMutation(gql`
mutation loginBenutzer($kuerzel: String!, $passwort: String!) {
loginBenutzer(kuerzel: $kuerzel, passwort: $passwort) {
user {
kuerzel: BEN_MIA_KUERZEL,
name: BEN_NAME
},
token
}
}
`)
function login() {
if (form.value.validate()) {
loginBenutzer({ kuerzel: username.value, passwort: password.value })
.then(data => {
useUserLogin(data.data.loginBenutzer)
root.$router.push('/hm')
})
.catch(err => {
errorMessage.value = err.message
console.log(err)
})
}
}
and the type like this
type Mutation {
loginBenutzer(kuerzel: String!, passwort: String!): LoginResponse!,
}
but I am very sure, the useQuery for the calling of user information is a right way.
result: result data object.
As documentation said,result return result data object, which will be a Ref type,
refetch(variables?): Execute the query again, optionally with new
variables.
Try refetch instead if you want to pass new variables

relay subscription onNext not triggered on react-native

I am a subscription setup but onNext is not getting triggered I am not sure why since this is my first time implementing subscription and docs was not much help with the issue.
Here are the code implementations:
import {
graphql,
requestSubscription
} from 'react-relay'
import environment from '../network';
const subscription = graphql`
subscription chatCreatedSubscription{
chatCreated{
id
initiate_time
update_time
support_id
category_id
email
name
}
}
`;
function chatCreated(callback) {
const variables = {};
requestSubscription(environment, {
subscription,
variables,
onNext: () => {
console.log("onNext");
callback()
},
updater: () => {
console.log("updater");
}
});
}
module.exports = chatCreated;
and here is my network for the subscription
import { Environment, Network, RecordSource, Store } from "relay-runtime";
import Expo from "expo";
import { SubscriptionClient } from "subscriptions-transport-ws";
import { WebSocketLink } from 'apollo-link-ws';
import { execute } from 'apollo-link';
import accessHelper from "../helper/accessToken";
const networkSubscriptions = async (operation, variables) => {
let token = await accessHelper();
if (token != null || token != undefined) {
const subscriptionClient = new SubscriptionClient("ws://localhost:3000/graphql",
{
reconnect: true,
connectionParams: {
Authorization: token,
},
});
execute(new WebSocketLink(subscriptionClient), {
query: operation.text,
variables,
});
}
}
const network = Network.create(fetchQuery, networkSubscriptions);
const store = new Store(new RecordSource());
const environment = new Environment({
network,
store
});
export default environment;
the subscription is called in a componentDidMount method on a component it executes but the onNext method inside the subscription is never triggered when new information is added to what the subscription is listening to.
so i figured out that my issue was the network js not being setup properly and the version of subscription-transport-ws. i added version 0.8.3 of the package and made the following changes to my network file:
const networkSubscriptions = async (config, variables, cacheConfig, observer) => {
const query = config.text;
let token = await accessHelper();
if (token != null || token != undefined) {
const subscriptionClient = new SubscriptionClient(`ws://${api}/graphql`,
{
reconnect: true,
connectionParams: {
Authorization: token,
},
});
subscriptionClient.subscribe({ query, variables }, (error, result) => {
observer.onNext({ data: result })
})
return {
dispose: subscriptionClient.unsubscribe
};
}
}
i hope this helps you if you get stuck with the same issue as mine.

How to make Testing with Angular 6 and Apollo client for GraphQl

I'm trying to make test development with Angular 6 and GraphQl but we really don't know how to do as the best way possible. I have tried to find something on the internet that explains this, but nothing really good has been found.
I'll post my case here looking for someone who could help me to do, or someone who could tell me any tutorial/web to find more and good information.
The problem
I want to test an Auth. I have an auth.service.js and the respective spec.js. You can see it below:
AUTH_SERVICE_TS
import { Injectable } from '#angular/core';
import { Store } from '#ngrx/store';
import * as UserActions from './../../store/user/actions';
import gql from 'graphql-tag';
import { Apollo } from 'apollo-angular';
import { Router } from '#angular/router';
#Injectable({
providedIn: 'root'
})
export class AuthService {
user;
constructor(private store: Store<any>, private apollo: Apollo, private router: Router) {
this.store.select('state').subscribe(state => this.user = state);
}
/**
* Function that check the email and password for login
* #param email
* #param password
*/
login(email: string, password: string) {
this.apollo.mutate({
mutation: this.loginRequestGql(),
variables: {
email: email,
password: password
}
}).subscribe(value => {
const data = value.data.login;
this.saveUserData(data);
this.router.navigate(['/app']);
});
}
/**
* Function that save user data in the store and in the session storage
* #param data
*/
saveUserData(data) {
this.store.dispatch(new UserActions.Login({token: data.token}));
this.setSessionStorage(this.user);
}
/**
* Function that remove user info in the store
*/
logout() {
this.store.dispatch(new UserActions.Logout());
this.setSessionStorage(this.user);
}
/**
* Function that create the request with Graphql sintax
*/
loginRequestGql() {
return gql`
mutation Login($email: String!, $password: String!) {
login(email: $email, password: $password) {
token
}
}
`;
}
/**
* Function that save in the session storage the data parameter
* #param data
*/
setSessionStorage(data) {
sessionStorage.setItem('session', JSON.stringify(data));
}
}
AUTH_SERVICE_SPEC_TS
import { TestBed, inject } from '#angular/core/testing';
import { ApolloTestingController, ApolloTestingModule } from "apollo-angular/testing";
import { RouterTestingModule } from '#angular/router/testing';
import { AuthService } from './auth.service';
import { Store, StoreModule } from '#ngrx/store';
import { reducer } from '../../store/user/reducer';
describe('AuthService', () => {
let backend: ApolloTestingController;
let authService: AuthService;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [ RouterTestingModule, ApolloTestingModule, StoreModule.forRoot({ state: reducer }) ],
providers: [ AuthService,
{ provide: ApolloTestingModule, useClass: ApolloTestingModule }
]
});
});
beforeEach(() => {
backend = TestBed.get(ApolloTestingController);
authService = TestBed.get(AuthService);
});
it('should be created', inject([AuthService], (service: AuthService) => {
expect(service).toBeTruthy();
}));
it('should test login', (done) => {
const email = 'diego#mail.com';
const password = '123456';
// const a = authService.login(email, password);
// expect(a).toBe(TEST_RESPONSE['data'].login.token);
// authService.login(email, password);
// backend.expectOne(authService.loginRequestGql).flush(TEST_RESPONSE);
});
});
const TEST_RESPONSE: Object = {
"data": {
"login": {
"token": "eyJ0eXAiOiJKLCJhbGciOiJSUzI1NiIsImp0aSI6IjZjZDBjMDMXX0.as7-r_nlYfJ2w3CfOqwtLcTlBg5LrwFcm_ZXZ_GzCl5Qq0GS92r5tqGJtFzRfG02PPoLZ8uwsbgLj-5v2pYBXHjBLZvbjnW_zgXRLoDEcrBDpfPAoVH85ca_hb_xVaIgEUGumUPfn2IOx0Ce8fLlqtWGqoWtWzcCE
}
};
Thanks in advance to the community!! Hope you can help me!!
PD: If you need more information, just request and i'll give.
In one of recent versions of apollo-angular we released a testing utilities. Testing technique is pretty much similar to how you test HttpClient in Angular.
To learn more about how to test components and services that uses Apollo, please read the official documentation about it.
https://www.apollographql.com/docs/angular/guides/testing.html
It seems we cannot use expectOne with a simple DocumentNode parameter when doing a mutation.
So instead of:
backend.expectOne(authService.loginRequestGql).flush(TEST_RESPONSE);
we must pass to expectOne a function which asserts the operation's query definition is the expected one:
backend.expectOne((operation) => {
expect(operation.query.definitions).toEqual(mutation.definitions);
return true;
})
.flush(TEST_RESPONSE);

How to update state in reducer

I have a problem with my reducer. I am using redux to create a login page. I have successfully logged in yet I failed to navigate to the next page. The state in my reducer is not updated. How do I solve this?
This is how I wrote my reducer:
import { LOGIN_SUCCESS } from '../actions/types';
const INITIAL_STATE={
isLoginSuccess:false,
}
export default function (state=INITIAL_STATE, action){
switch(action.type){
case LOGIN_SUCCESS:
return {
isLoginSuccess : true
}
default:
return INITIAL_STATE;
}
}
This is how I wrote my action:
import axios from 'axios';
import * as helper from '../common';
import { LOGIN_SUCCESS } from './types';
export const attemptLogin = (username, password) => async dispatch => {
let param = {
txtNomatrik: username,
txtPwd: password,
public_key: helper.PUBLIC_KEY,
secret_key: helper.SECRET_KEY
}
console.log(`${helper.ROOT_API_URL}/v1/basic/ad/std/login`)
let login_res = await
axios.post(`${helper.ROOT_API_URL}/v1/basic/ad/std/login`, param)
console.log(login_res.data);
if (login_res.data.status == 'Successful Login') {
const { login } = login_res.data;
await AsyncStorage.seItem('Login_token', username);
await AsyncStorage.setItem('profile', JSON.stringify(login));
dispatch({ type: LOGIN_SUCCESS, payload: { isLoginSuccess : true } });
}
}
I want to use the isLoginSuccess in my index file to navigate login to the next page like this:
componentWillReceiveProps(nextProps){
if(nextProps.isLoginSuccess){
this.props.navigation.navigate('logged');
}
}
Below is how I connect redux:
const mapStateToProps = ({ auth }) => {
return {
isLoginSuccess: auth.isLoginSuccess
}
}
export default connect(mapStateToProps, actions)(LoginScreen);
Below is my combineReducer file:
import {combineReducers} from 'redux';
import news from './welcome_reducer';
import auth from './login_reducer';
export default combineReducers({
news,
auth
})
How do I solve this? I feel lost now, have tried a lot of ways to solve it . The Api call for login is successful but the action is not dispatched at all. I can't update the state and I can't navigate to the next page. Please help me