Как создать утилиты с аполлон-хуками для реактивной системы - PullRequest
0 голосов
/ 11 июля 2019

Я делаю аутентификационный поток с двумя токенами (accessToken, refreshToken) в Reaction-native / apollo-hooks / graphql. И создал аполлон-хук для обновления токенов. И этот хук мне нужно использовать в разных компонентах, потому что каждый раз, когда пользователь хочет сделать какие-либо действия, такие как сделать пост или комментарий, мне нужно вызвать updateTokens apollo hook. Поэтому мне нужно перенести всю логику updateTokens на отдельные утилиты, потому что я не хочу повторяться и ставить везде один и тот же код. Но когда я делаю отдельный файл с утилитой Apollo, он не работает. Может кто-нибудь объяснить мне, как я могу сделать это, пожалуйста!

import React from 'react'
import { View, Button, Text } from 'react-native'
import { useMutation } from 'react-apollo-hooks'
import gql from 'graphql-tag'
import * as Keychain from 'react-native-keychain'

const UPDATE_TOKENS = gql`
  mutation UpdateTokens($accessToken: String!, $refreshToken: String!) {
    updateTokens(data: { accessToken: $accessToken, refreshToken: $refreshToken }) {
      user {
        name
        phone
      }
      accessToken
      refreshToken
    }
  }
`

const HomeScreen = ({ navigation }) => {
  const update_tokens = useMutation(UPDATE_TOKENS)

  const updateTokens = (accessToken, refreshToken) => {
    console.log('accessToken', accessToken)
    console.log('refreshToken', refreshToken)
    update_tokens({
      variables: { accessToken, refreshToken },
      update: async (cache, { data }) => {
        const accessToken = data.updateTokens.accessToken
        const refreshToken = data.updateTokens.refreshToken
        await Keychain.setGenericPassword(accessToken, refreshToken)
      }
    }).then(() => console.log('We have new credentials'))
  }

  const getCredentials = async () => {
    const tokens = await Keychain.getGenericPassword()
    console.log('tokens', tokens)
    const keyChainAccessToken = tokens.username
    const keyChainRefreshToken = tokens.password
    console.log('keyChainAccessToken', keyChainAccessToken)
    console.log('keyChainRefreshToken', keyChainRefreshToken)
    updateTokens(keyChainAccessToken, keyChainRefreshToken)
  }

  return (
    <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
      <Text>Home Screen</Text>
      <Button title="updateTokens" onPress={getCredentials} />
      <Button title="getCredentials" onPress={checkCredentials} />
      <Button title="SignOut" onPress={userSignOut} />
    </View>
  )
}
export { HomeScreen }

Могу ли я поместить его в отдельный файл, как это, и использовать его после в разных местах:

import { useMutation } from 'react-apollo-hooks'
import gql from 'graphql-tag'
import * as Keychain from 'react-native-keychain'
import jwtDecode from 'jwt-decode'

const UPDATE_TOKENS = gql`
  mutation UpdateTokens($refreshToken: String!, $refreshTokenId: String!) {
    updateTokens(refreshToken: $refreshToken, refreshTokenId: $refreshTokenId) {
      user {
        name
        phone
      }
      accessToken
      refreshToken
    }
  }
`

const updateCredentials = () => {
  const update_tokens = useMutation(UPDATE_TOKENS)
  const updateTokens = (refreshToken, refreshTokenId) => {
    console.log('refreshToken', refreshToken)
    console.log('refreshTokenId', refreshTokenId)
    update_tokens({
      variables: { refreshToken, refreshTokenId },
      update: async (cache, { data }) => {
        const newAccessToken = data.updateTokens.accessToken
        const newRefreshToken = data.updateTokens.refreshToken
        const user = data.updateTokens.user
        await Keychain.setGenericPassword(newAccessToken, newRefreshToken)
      }
    }).then(() => console.log('We have new credentials'))
  }

  const getCredentials = async () => {
    try {
      const tokens = await Keychain.getGenericPassword()
      console.log('tokens', tokens)
      const keychainAccessToken = tokens.username
      const keychainRefreshToken = tokens.password
      const currentTime = Date.now() / 1000
      const decodeAccessToken = jwtDecode(keychainAccessToken)
      console.log('decodeAccessToken', decodeAccessToken)
      const keychainRefreshTokenId = decodeAccessToken.refreshTokenId
      console.log('keychainRefreshTokenId', keychainRefreshTokenId)

      if (decodeAccessToken.exp > currentTime) {
        console.log('Credentials is still valid')
      } else if (decodeAccessToken.exp < currentTime) {
        console.log('Go to Update!')
        updateTokens(keychainRefreshToken, keychainRefreshTokenId)
      }
    } catch (err) {
      throw new Error('Invalid credentials')
    }
  }
}
export { updateCredentials }

Ответы [ 2 ]

0 голосов
/ 11 июля 2019

если вы все еще хотите это сделать, вы можете сделать это HOC-способом.

import React from 'react';
import YOUR_FUNCTIONS from '~/functions_location';

const withRefreshToken = () => (Component) => {
    return <Component {...YOUR_FUNCTIONS} />
};

export {
    withRefreshToken
}

после этого вы можете сделать это следующим образом:

export default withRefreshToken(HomeScreen);
0 голосов
/ 11 июля 2019

Для меня, я не буду делать это для обновления токена.Я сделал промежуточное ПО для него.

Как установить промежуточное ПО:

import { createUploadLink } from 'apollo-upload-client';
const getClient = ({ userStore: { token, refreshToken: refresh_token }, lang }, dispatch) => {
    const locale = lang || 'en';

    const uploadLink = createUploadLink({
        uri: 'http://localhost:4000'
    });

    const client = new ApolloClient({
        link: ApolloLink.from([
            getTokensMiddleware(token, refresh_token, locale, dispatch),
            uploadLink
        ]),
        cache: new InMemoryCache()
    });

    return client;
};

Промежуточное программное обеспечение:

const getTokensMiddleware = (token, refresh_token, locale, dispatch) => {
    return setContext(async (req, { headers, ...others }) => {
        if (!token || !refresh_token) return {};
        var decoded = jwtDecode(token);
        const isExpired = decoded.exp <= Date.now() / 1000 + 120;
        var decodedRefresh = jwtDecode(refresh_token);
        const isRefreshJWTExpired = decodedRefresh.exp <= Date.now() / 1000;
        if(isRefreshJWTExpired) return {};
        if (!isExpired) {
            return {
                ...others,
                headers: {
                    ...headers,
                    Authorization: token ? `Bearer ${token}` : '',
                    locale
                }
            };
        }
        return new Promise((success, fail) => {
            refreshToken(refresh_token)
                .then(response => {
                    if (response.ok) {
                        return response.json();
                    } else {
                        fail(response);
                    }
                })
                .then(json => {
                    const { token } = json.data.refreshToken;
                    dispatch({type: "login", payload: json.data.refreshToken})
                    success({
                        ...others,
                        headers: {
                            ...headers,
                            Authorization: token ? `Bearer ${token}` : '',
                            locale
                        }
                    });
                });
        });
    });
};

Ваша функция обновления:

const refreshToken = refreshToken => {
    const data = {
        operation: 'RefreshTokenMutation',
        query:
            'mutation RefreshTokenMutation($email: String!, $refreshToken: String!) {  refreshToken(email: $email, refreshToken: $refreshToken) {    token    refreshToken    user {      id      email      username      displayname      role {        title        permissions        __typename      }      __typename    }    __typename  }}',
        variables: { email: 'admin_email', refreshToken: refreshToken }
    };
    return fetch('http://localhost:4000/', {
        method: 'POST',
        headers: { 'content-type': 'application/json' },
        body: JSON.stringify(data)
    });
};

Как ее использовать

const AppPage = () => {
    const { state, dispatch } = useStore(); //a hook for getting context containing useReducer
    return (
        <I18nextProvider i18n={i18n}>
            <Helmet>
                <title>My Project</title>
                <meta name="description" content="My project" />
            </Helmet>
            <ApolloProvider client={getClient(state, dispatch)}>
                <LocaleProvider locale={state.antdLocale}>
                    <App />
                </LocaleProvider>
            </ApolloProvider>
        </I18nextProvider>
    );
};
...