ADVPL + REACT – Parte 05 | Finalizando API de CRUD

Introdução

Dando continuidade a série de artigos sobre ReactJS + Advpl, hoje irei finalizar a API de CRUD com as funcionalidades de incluir, alterar e deletar permissões na tabela Z20. Caso você tenha perdido alguma parte ou tenha interesse em ler os primeiros artigos dessa série, abaixo você tem uma lista contendo o título e o link de cada um deles.

Finalizando CRUD

Agora iremos criar as APIs responsáveis por incluir, alterar e deletar permissões de acessos, fechando assim o CRUD do nosso backend.

Fiquem a vontade para incluir novas validações, funcionalidades ou abstrair ainda mais as responsabilidades de cada função, farei de uma forma simples para ficar mais didático.

Bora Codar!!!

Logo de inicio vamos partir para a funcionalidade de inclusão, para isso vamos incluir o método POST no controller.

API POST

TPontinAccessControl.prw

WSMETHOD POST DESCRIPTION "Realiza o cadastro de controle de acessos" ;
    WSSYNTAX "/TPontinAccessControl"

No código acima informamos que iremos trabalhar com o método POST e abaixo temos o código para receber a requisição POST e processa-lo:

WSMETHOD POST WSRECEIVE WSSERVICE TPontinAccessControl

  Local cBody     := ""
  Local oDAO      := Nil
  Local oRetorno  := Nil

  //|Recupera os dados do body |
  cBody := ::GetContent()

  //|Realiza a inclusão do registro |
  oDAO      := TPontinAccessControlDAO():New()
  oRetorno  := oDAO:CriarZ20(cBody)

  //|Defino que o retorno sera em JSON |
  ::SetContentType("application/json")

  ::SetStatus( oRetorno['CodRet'] )
  ::SetResponse( oRetorno:ToJson() )

Return .T.

Percebam que novamente passamos a responsabilidade de acessar os dados para o DAO, deixando o controller apenas com a responsabilidade de receber a requisição, repassar para o DAO e retornar a resposta.

Vamos agora alterar o DAO para criar o método “CriarZ20” utilizado acima:

TPontinAccessControlDAO.prw

Class TPontinAccessControlDAO From LongClassName

  Data oModel

  Method New() Constructor
  Method ListarZ20()
  Method CriarZ20()

EndClass

No código acima declaramos o método CriarZ20 na classe e abaixo temos o código responsável por validar os dados enviados e incluir o registro na tabela Z20.

Method CriarZ20(cDados) Class TPontinAccessControlDAO

  Local oResponse   := JsonObject():New()
  Local oBody       := JsonObject():New()
  Local aZ20Struct  := {}
  Local cErro       := ""
  Local cCampo      := ""
  Local nI          := 0

  //|Monto o objeto Json |
  oBody:FromJson(cDados)

  //|Valida o body enviado |
  If ValType(oBody) != "U" .And. ValType(cDados) != "U"

    //|Valida campos obrigatórios |
    If ValType( oBody['Z20_ROTINA'] ) == "U" .Or. Empty( oBody['Z20_ROTINA'] )
      cErro := "Z20_ROTINA obrigatorio \n "
    EndIf

    If ValType( oBody['Z20_IDUSR'] ) == "U" .Or. Empty( oBody['Z20_IDUSR'] )
      cErro += "Z20_IDUSR obrigatorio \n "
    EndIf

    //|Valida os campos de permissão |
    For nI := 1 To 15

      cCampo      := "Z20_NUM" + cValToChar(nI)

      If ValType( oBody[cCampo] ) == "U" .Or. Empty( oBody[cCampo] )
        cErro += cCampo + " obrigatorio \n "
      EndIf

    Next nI

  Else

    cErro := "Body invalido ou inexistente \n "

  EndIf

  //|Valida se já existe permissão para o usuario nessa rotina |
  dbSelectArea("Z20")
  Z20->( dbSetOrder(1) )
  If Z20->( dbSeek( xFilial("Z20") + oBody['Z20_CODROT'] + oBody['Z20_IDUSR'] ) )

    cErro := "Usuario informado ja possui permissoes nessa rotina \n "

  EndIf


  //|Realizo a inclusão do registro |
  If Empty(cErro)

    //|Para evitar erros, vamos nos basear no dicionario de dados |
    aZ20Struct  := Z20->( dbStruct() )

    RecLock("Z20", .T.)

    //|Monto o objeto JSON com a estrutura da Z20 |
    For nI := 1 To Len(aZ20Struct)

      cCampo      := aZ20Struct[nI,1]

      //|Valida se o campo existe e está com o tipo certo |
      If ValType( oBody[cCampo] ) == aZ20Struct[nI,2]
        Z20->&(  cCampo )   := oBody[cCampo]
      EndIf

    Next nI

    Z20->( MsUnLock() )

    oResponse['CodRet']   := 201
    oResponse['result']   := 'Registro incluido com sucesso'
    oResponse['RECNO']    := Z20->( Recno() )

  Else

    //|Retorna erros de validação |
    oResponse['CodRet']   := 400
    oResponse['error']    := JsonObject():new()
    oResponse['error']    := cErro

  EndIf

Return oResponse

Explicando por partes…

 //|Monto o objeto Json |
  oBody:FromJson(cDados)
 
  //|Valida o body enviado |
  If ValType(oBody) != "U" .And. ValType(cDados) != "U"
 
    //|Valida campos obrigatórios |
    If ValType( oBody['Z20_ROTINA'] ) == "U" .Or. Empty( oBody['Z20_ROTINA'] )
      cErro := "Z20_ROTINA obrigatorio \n "
    EndIf
 
    If ValType( oBody['Z20_IDUSR'] ) == "U" .Or. Empty( oBody['Z20_IDUSR'] )
      cErro += "Z20_IDUSR obrigatorio \n "
    EndIf
 
    //|Valida os campos de permissão |
    For nI := 1 To 15
 
      cCampo      := "Z20_NUM" + cValToChar(nI)
 
      If ValType( oBody[cCampo] ) == "U" .Or. Empty( oBody[cCampo] )
        cErro += cCampo + " obrigatorio \n "
      EndIf
 
    Next nI
 
  Else
 
    cErro := "Body invalido ou inexistente \n "
 
  EndIf

Linha 02: oBody:FromJson(cDados)
O método “FromJson” transforma o body recebido em um objeto no formato JSON.

Linha 08 a 14: If ValType( oBody[‘Z20_ROTINA’] ) == “U”
Realizo a validação de informações obrigatórias para permitir incluir uma nova permissão, como o Id do usuário que receberá a permissão e em qual rotina essa permissão será utilizada.

Linha 17 a 25: For nI := 1 To 15
Asseguro que todos os 15 campos de permissões foram enviados, validando de uma forma mais dinâmica utilizando o FOR.

Qualquer quebra de requisito da rotina será concatenado na variável “cErro” para que no final possa ser retornado ao controller.

//|Para evitar erros, vamos nos basear no dicionario de dados |
    aZ20Struct  := Z20->( dbStruct() )

    RecLock("Z20", .T.)

    //|Monto o objeto JSON com a estrutura da Z20 |
    For nI := 1 To Len(aZ20Struct)

      cCampo      := aZ20Struct[nI,1]

      //|Valida se o campo existe e está com o tipo certo |
      If ValType( oBody[cCampo] ) == aZ20Struct[nI,2]
        Z20->&(  cCampo )   := oBody[cCampo]
      EndIf

    Next nI

    Z20->( MsUnLock() )

    oResponse['CodRet']   := 201
    oResponse['result']   := 'Registro incluido com sucesso'
    oResponse['RECNO']    := Z20->( Recno() )

Se tudo estiver correto na validação, o código acima realiza a inclusão do registro na tabela Z20 utilizando novamente a classe dbStruct para garantir que os dados enviados são compatíveis com o dicionário de dados da Z20.

Após a inclusão, monta o objeto JSON “oResponse” informando ao controller que o registro ocorreu com sucesso e também o Recno do registro inserido.

API DELETE

TPontinAccessControl.prw

WSMETHOD DELETE DESCRIPTION "Deleta um cadastro de controle de acessos" ;
    WSSYNTAX "/TPontinAccessControl/{recno}"

Para realizar a exclusão do registro no banco de dados, precisamos que seja enviado via “Path Params” o Recno do registro a ser excluído.

WSMETHOD DELETE WSRECEIVE WSSERVICE TPontinAccessControl

  Local nRecno    := 0
  Local oDAO      := Nil
  Local oRetorno  := Nil

  //|Recupera o recno a ser deletado |
  If Len(::aURLParms) > 0

    nRecno  := Val(::aURLParms[1])

  EndIf

  //|Realiza a inclusão do registro |
  oDAO      := TPontinAccessControlDAO():New()
  oRetorno  := oDAO:RemoverZ20(nRecno)

  //|Defino que o retorno sera em JSON |
  ::SetContentType("application/json")

  ::SetStatus( oRetorno['CodRet'] )
  ::SetResponse( oRetorno:ToJson() )

Return .T.

Novamente o controller fica com a função de receber a requisição, repassar para o DAO e retornar o resultado.

TPontinAccessControlDAO.prw

Class TPontinAccessControlDAO From LongClassName

  Data oModel

  Method New() Constructor
  Method ListarZ20()
  Method CriarZ20()
  Method RemoverZ20()

EndClass
Method RemoverZ20(nRecno) Class TPontinAccessControlDAO

  Local oResponse   := JsonObject():New()

  dbSelectArea("Z20")
  Z20->( dbSetOrder(1) )
  Z20->( dbGoTo(nRecno) )

  If !Z20->( EoF() )

    RecLock("Z20", .F.)
    Z20->( dbDelete() )
    Z20->( MsUnLock() )

    oResponse['CodRet']   := 200
    oResponse['result']   := 'Registro excluido com sucesso'

  Else

    //|Retorna erros de validação |
    oResponse['CodRet']   := 400
    oResponse['error']    := JsonObject():new()
    oResponse['error']    := 'Recno informado nao existe na base de dados'

  EndIf

Return oResponse

Método “RemoverZ20” é bem simples, posiciona no Recno passado e deleta o registro. Ao final informa ao controller se conseguiu realizar a exclusão ou se foi informado um Recno inexistente.

API PUT

TPontinAccessControl.prw

WSMETHOD PUT DESCRIPTION "Altera um cadastro de controle de acessos" ;
    WSSYNTAX "/TPontinAccessControl/{recno}"

Igual no método DELETE, no PUT também esperamos que seja enviado via “Path Params” o Recno do registro a ser alterado.

WSMETHOD PUT WSRECEIVE WSSERVICE TPontinAccessControl

  Local nRecno    := 0
  Local cBody     := ""
  Local oDAO      := Nil
  Local oRetorno  := Nil

  //|Recupera o recno a ser deletado |
  If Len(::aURLParms) > 0

    nRecno  := Val(::aURLParms[1])

  EndIf

  //|Recupera os dados do body |
  cBody := ::GetContent()

  //|Realiza a inclusão do registro |
  oDAO      := TPontinAccessControlDAO():New()
  oRetorno  := oDAO:AlterarZ20(nRecno, cBody)

  //|Defino que o retorno sera em JSON |
  ::SetContentType("application/json")

  ::SetStatus( oRetorno['CodRet'] )
  ::SetResponse( oRetorno:ToJson() )

Return .T.

O controller recebe a requisição, passa para o DAO e retorna o resultado.

TPontinAccessControlDAO.prw

Class TPontinAccessControlDAO From LongClassName

  Data oModel

  Method New() Constructor
  Method ListarZ20()
  Method CriarZ20()
  Method RemoverZ20()
  Method AlterarZ20()

EndClass
Method AlterarZ20(nRecno, cDados) Class TPontinAccessControlDAO

  Local oResponse   := JsonObject():New()
  Local oBody       := JsonObject():New()
  Local aZ20Struct  := {}
  Local cErro       := ""
  Local cCampo      := ""
  Local nI          := 0

  dbSelectArea("Z20")
  Z20->( dbSetOrder(1) )
  Z20->( dbGoTo(nRecno) )

  //|Valida se é um recno valido |
  If !Z20->( EoF() )

    //|Monto o objeto Json |
    oBody:FromJson(cDados)

    //|Valida o body enviado |
    If ValType(oBody) != "U" .And. ValType(cDados) != "U"

      //|Valida campos obrigatórios |
      If ValType( oBody['Z20_ROTINA'] ) != "U" .And. Empty( oBody['Z20_ROTINA'] )
        cErro := "Z20_ROTINA nao pode ser vazio \n "
      EndIf

      If ValType( oBody['Z20_IDUSR'] ) != "U" .And. Empty( oBody['Z20_IDUSR'] )
        cErro += "Z20_IDUSR nao pode ser vazio \n "
      EndIf

      //|Valida os campos de permissão |
      For nI := 1 To 15

        cCampo      := "Z20_NUM" + cValToChar(nI)

        If ValType( oBody[cCampo] ) != "U" .And. Empty( oBody[cCampo] )
          cErro += cCampo + " nao pode ser vazio \n "
        EndIf

      Next nI

    Else

      cErro := "Body invalido ou inexistente \n "

    EndIf

    //|Realiza a alteração do registro |
    If Empty(cErro)

      //|Para evitar erros, vamos nos basear no dicionario de dados |
      aZ20Struct  := Z20->( dbStruct() )

      RecLock("Z20", .F.)

      //|Monto o objeto JSON com a estrutura da Z20 |
      For nI := 1 To Len(aZ20Struct)

        cCampo      := aZ20Struct[nI,1]

        //|Valida se o campo existe e está com o tipo certo |
        If ValType( oBody[cCampo] ) == aZ20Struct[nI,2]
          Z20->&(  cCampo )   := oBody[cCampo]
        EndIf

      Next nI

      Z20->( MsUnLock() )

      oResponse['CodRet']   := 200
      oResponse['result']   := 'Registro alterado com sucesso'
      oResponse['RECNO']    := Z20->( Recno() )

    EndIf

  Else

    //|Retorna erros de validação |
    cErro := 'Recno informado nao existe na base de dados'

  EndIf

  If !Empty(cErro)

    //|Retorna erros de validação |
    oResponse['CodRet']   := 400
    oResponse['error']    := JsonObject():new()
    oResponse['error']    := cErro

  EndIf

Return oResponse

No método “AlterarZ20” precisamos fazer validações básicas nos campos obrigatórios e estando tudo certo podemos alterar o registro solicitado de acordo com os campos enviados no body.

Esse método irá alterar apenas os campos que forem enviados no body, não sendo necessário enviar todos.

Hora da verdade

Vamos agora compilar tudo e testar no Insomnia se o comportamento do nosso CRUD está de acordo com o programado.

POST – Criar permissão

Informamos no body tipo JSON todos os campos que queremos que sejam alimentados na inclusão e estando tudo certo recebemos o retorno 201 e o Recno do registro incluído.

DELETE – Deletar permissão

Para deletar não precisamos enviar o body na requisição, mas precisamos informar o Recno via “Path Params” para que o DAO saiba qual registro queremos excluir.

PUT – Alterar permissão

Já na alteração precisamos enviar o body na requisição informando quais campos queremos alterar com suas novas informações e o Recno via “Path Params” para que o DAO saiba qual registro queremos alterar.

Com isso finalizamos o desenvolvimento do nosso CRUD em ADVPL e já podemos começar a desenvolver o frontend da aplicação utilizando o REACJS.

Espero que estejam gostando do material e caso tenha dúvidas ou sugestões fiquem a vontade para enviar nos comentários.

Lembrando que os fontes estão disponíveis para todos no GitHub.

O nosso objetivo é criar uma cultura de uso da tecnologia para tornar as empresas brasileiras mais competitivas e os desenvolvedores cada vez mais capacitados para o mercado de trabalho, por isso compartilhem esses posts com seus amigos para que possamos atingir um número maior ainda de pessoas.

Até mais…

gostou? siga e compartilhe

Deixe seu comentário

error

Siga para receber novidades

Registrar
LinkedIn
LinkedIn
Share
Instagram