Consumo API Curió no React Native

Setup do Curió no App

1º Passo:

Criar uma pasta de configuração na raiz do projeto.

image-1668707401515.png


2º Passo:

Criar um arquivo de configuração na pasta config criada.

Exemplo:

{
  "service": {
    "url": "https://srvd1.dev.evologica.com.br/cxClient/cxIsapiClient.dll/gatewayJSON?version=3",
    "server": "192.168.0.49", // Trocar para o IP do PC que está rodando o serviço
    "system": 53,
    "port": 5563
  },
  "accessToken": "2CEAE6DE538FB283817C48F449C49D5D7D2BDE2A886406FCD6C5566DB34CE8EA",
  "resources": {
    "get": "https://srvd1.dev.evologica.com.br/cxClient/cxIsapiClient.dll/getpr?id=",
    "put": "https://srvd1.dev.evologica.com.br/cxClient/cxIsapiClient.dll/putpr",
    "viewer": "https://srvd1.dev.evologica.com.br/relatorios/viewer/rel.html?id="
  },
  "logs": true
}

3º Passo:

Criar uma pasta chamada core dentro da pasta src do projeto.


4º Passo:

Criar uma pasta chamada curio dentro da pasta core, e, dentro da pasta curio, adicione os dois arquivos index.ts e SessionManager.ts conforme a imagem.


5º Passo:

Dentro do arquivo index.ts adicione o código:

import { Platform } from 'react-native'
import { Dispatch } from 'redux'

import { FetchLink } from '@curio/client/links'
import { V3RequestParser } from '@curio/client/parsers'
import { createV3Validator } from '@curio/client/validators'
import config from '../../config/default.json'

import { SessionManager } from './SessionManager'
import { store } from '../redux'

let sessionManager: SessionManager

const createSessionManager = (dispatch: Dispatch, configuration = config) => {
  const link = new FetchLink(configuration.service)
  const requestParser = new V3RequestParser(configuration.service)
  sessionManager = new SessionManager(
    configuration.service,
    (request) => {
      return Promise.resolve(request)
        .then(requestParser.parse)
        .then(JSON.stringify)
        .then(link.parse)
        .then(link.post)
        .then((response) => response.json())
        .then(json => { return json })
        .then(createV3Validator(request).validate)
    },
    dispatch
  )

  interface Attributes {
    [index: string]: string
    _Id: string
    _Recipient: string
    _Sender: string
    _SerialNumber: string
    _SignalName: string
  }

  if (config.logs) {
    sessionManager[Symbol.observable]().subscribe({
      next: (msg) => {
        if (!Number(msg.signalName)) {
          const signalName = msg.signalName.substr(msg.signalName.indexOf('_') + 1).replace(/_/g, ' ')
          const filteredBody = Object.keys(msg.body ? (msg.body as Attributes) : {}).reduce((acc, curr) => {
            if (curr.indexOf('_') < 0) return { ...acc, [curr]: (msg.body as Attributes)[curr] }
            else return acc
          }, {})

          const colorType = msg.type === 'response' ? 'green' : 'blue'
          // console.log(
          //   `%c ${msg.type.toUpperCase()} %c ${signalName}`,
          //   `color: white; font-weight: bolder; background: ${colorType}`,
          //   'font-weight: bolder; font-style: italic;',
          //   Object.keys(filteredBody).length !== 0 ? filteredBody : ''
          // )
        }
      },
      error: (msg) => {
        if (!Number(msg.signalName)) {
          const signalName = msg.signalName.substr(msg.signalName.indexOf('_') + 1).replace(/_/g, ' ')
          const colorType = 'red'
          // console.log(
          //   `%c ${msg.type.toUpperCase()} %c ${signalName}`,
          //   `color: white; font-weight: bolder; background: ${colorType}`,
          //   'font-weight: bolder; font-style: italic;',
          //   msg.error.message
          // )
        }
      },
      complete: () => console.log('complete'),
    })
  }
}

export { sessionManager, createSessionManager }

export default config

6º Passo:

Dentro do arquivo SessionManager.ts adicione o código:

import { Dispatch } from 'redux'
import {
  MainUseCase,
  RequestDriver,
  SecurityManager,
  Service,
  UseCase,
  UseCaseMessageType
} from '@curio/client'
import { ConnectionError, DestinataryNotFoundError } from '@curio/client/errors'
import { ABORT, OPEN } from '@curio/client/utils/constants'
import { actions as sessionActions } from '../redux/session'
import AsyncStorage from '@react-native-async-storage/async-storage'

export const STORAGE_SESSION_KEY = 'br.com.environlink.sensor' // Trocar essa chave para usar a da aplicação que desejar

export class AnonymousSession extends MainUseCase {}

export class SessionManager extends SecurityManager {
  public session: MainUseCase | undefined
  private _dispatch: Dispatch
  constructor(service: Service, driver: RequestDriver, dispatch: Dispatch) {
    super(service, driver)
    this._dispatch = dispatch
  }

  public async openAnonymousUseCase(useCaseId: string | number): Promise<UseCase> {
    const id = await this.sendRequest(OPEN, {
      USECASEID: useCaseId,
      GUIID: 0,
      SYSTEM_CODE: this.service.system.toString(),
    })
    const uc = new UseCase(id)
    uc.driver = this.driver
    return uc
  }

  public async openMainUseCase<U extends typeof MainUseCase>(username: string, password: string, constructor?: U) {
    this.session = await super.openMainUseCase(username, password, constructor)
    await AsyncStorage.setItem(STORAGE_SESSION_KEY, this.session!.token.toString())

    this.session!.addListener(ABORT, () => this._unauthenticate(false))
    this.session!.addListener(UseCaseMessageType.RESPONSE, (message: any) => {
      if (message.error instanceof DestinataryNotFoundError || message.error instanceof ConnectionError) {
        this._unauthenticate(true)
      }
    })
    return this.session! as InstanceType<U>
  }

  public connectMainUseCase(mainUseCase: MainUseCase) {
    this.session = mainUseCase
    if (!(mainUseCase instanceof AnonymousSession)) {
      AsyncStorage.setItem(STORAGE_SESSION_KEY, this.session!.token.toString()).then().catch(console.error)
    }
    this.session.addListener(ABORT, () => this._unauthenticate(false))
    this.session.addListener(UseCaseMessageType.RESPONSE, (message: any) => {
      if (message.error instanceof DestinataryNotFoundError || message.error instanceof ConnectionError) {
        this._unauthenticate(true)
      }
    })
    return super.connectMainUseCase(mainUseCase)
  }
  private _unauthenticate(expired: boolean) {
    AsyncStorage.removeItem(STORAGE_SESSION_KEY)
    if (expired) {
      this._dispatch(sessionActions.logout())
    }
    this.session = undefined
  }
}

Setup Redux no App

1º Passo:

Instalar as seguintes bibliotecas do Redux na aplicação:

  - react-redux
  - redux
  - @reduxjs/toolkit
  - redux-devtools-extension
  - redux-logger
  - redux-persist

OBS: Caso esteja usando Typescript, baixe as dependências de desenvolvimento a seguir:

  - @types/react-redux
  - @types/redux
  - @types/redux-logger

2º Passo

Criar uma pasta chamada redux com um arquivo index.ts e outro reducer.ts


Insira o código a seguir no arquivo index.ts

import { configureStore } from '@reduxjs/toolkit';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { rootReducer, initialState, StoreState } from './reducer'
import { createLogger } from 'redux-logger'

export * from './reducer'

AsyncStorage.removeItem('br.com.environlink.sensor.storage') // Trocar essa chave para usar a da aplicação que desejar

const loggerMiddleware = createLogger({
    collapsed: true
})

const store = configureStore<StoreState>({
    reducer: rootReducer,
    preloadedState: initialState,
});

export { store };

Insira o código a seguir no arquivo reducers.ts

import { combineReducers, PayloadAction } from '@reduxjs/toolkit'

import entities, { EntitiesState, initialState as entitiesInitialState } from './entities'
import { initialState as sessionInitialState, reducer as sessionReducer, State } from './session'
import ui, { initialState as uiInitialState, UIState } from './ui'

export interface StoreState {
  entities: EntitiesState
  session: State
  ui: UIState
}

export const initialState: StoreState = {
  session: sessionInitialState,
  ui: uiInitialState,
  entities: entitiesInitialState,
}

export const rootReducer = combineReducers<StoreState>({
  session: sessionReducer,
  ui,
  entities,
})

export { EntitiesTransform } from './entities'

Chamando API de Login

1º Passo:

Criar uma pasta chamada ui dentro da pasta redux e criar um arquivo index.ts nela contendo o código a seguir:

import { combineReducers } from 'redux'
import {
  initialState as contextInitialState,
  reducer as context,
  State as ContextState
} from './context'
import {
  initialState as credentialsInitialState,
  reducer as credentials,
  State as CredentialsState
} from './credentials'
import { initialState as errorInitialState, reducer as error, State as ErrorState } from './error'
import { initialState as loginInitialState, reducer as login, State as LoginState } from './login'

export * from './login'

export interface UIState {
  error: ErrorState
  login: LoginState
  credentials: CredentialsState
  context: ContextState
}

const reducer = combineReducers<UIState>({
  login,
  error,
  credentials,
  context,
})

export const initialState: UIState = {
  login: loginInitialState,
  error: errorInitialState,
  credentials: credentialsInitialState,
  context: contextInitialState,
}

export default reducer

2º Passo:

Criar uma pasta login dentro da pasta ui com 3 arquivos: index.ts , reducer.ts e selectors.ts


Insira o código a seguir no arquivo login/index.ts

import { getTypesActions } from '../../../utils/reduxMethods'
import login, { actions } from './reducer'
import * as selectors from './selectors'

export * from './reducer'
const reducer = login.reducer
const types = getTypesActions(actions)

export { actions, reducer, types, selectors }

export default login

Insira o código a seguir no arquivo login/reducers.ts

import { ActionType, reducerActions } from '../../../utils/reduxMethods'
import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit'
import { login } from '../../../core/api/useCases/login'
import { createSessionManager } from '../../../core/curio'

export interface State {
  login: boolean
  fetchingLogin: boolean
  error: boolean
}

export const initialState: State = {
  login: false,
  fetchingLogin: false,
  error: false
}

interface Login {
  username: string
  password: string
}

export interface ChangePassword {
  oldPassword: string
  newPassword: string
  onSuccess?(): void
}
type ChangePasswordReducer = ActionType<
  'changePasswordRequest' | 'changePasswordSuccess' | 'changePasswordError',
  ChangePassword | undefined
>


const loginAction = createAsyncThunk('ui/login/action', async (payload: Login, thunkApi) => {
  await login(payload.username, payload.password);
})

const initializeAction = createAsyncThunk('ui/login/initialize', (_, thunkApi) => {
  createSessionManager(thunkApi.dispatch);
})![](http://docs.dev.evologica.com.br/loading.gif#uploadimage-7f0ab48e7561d)

export const actions = { loginAction, initializeAction }

const loginReducer = createSlice({
  name: 'ui/login',
  initialState,
  reducers: {
  },
  extraReducers: (builder) => {
    builder.addCase(loginAction.pending, (state) => {
      state.fetchingLogin = true
      state.login = true
    });

    builder.addCase(loginAction.fulfilled, (state) => {
      state.fetchingLogin = false
    });

    builder.addCase(loginAction.rejected, (state) => {
      state.error = true
      state.fetchingLogin = false
    });
  }
})

export default loginReducer

Insira o código a seguir no arquivo login/selectors.ts

import { State } from './reducer'

export const isLogginIn = (state: State) => state.login

4ºPasso: Criar 2 estados utilizando useState(), um para username e outro

5ºPasso: No botão de login da sua tela, dispare a seguinte função:

onPress={() => dispatch(loginActions.loginAction({ username, password }) as any)}

.