Páginas filhas
  • 09. FWAdapterBaseV2

Versões comparadas

Chave

  • Esta linha foi adicionada.
  • Esta linha foi removida.
  • A formatação mudou.

Classe base para criação de adapters utilizados em serviços REST suportando filtros de paginação e filtros baseados no padrão oData.

Índice
maxLevel2

...

Métodos:

New

Descrição

Método construtor da classe

...

Retorno Logico, Se foi construído corretamente


...

AddMapFields

Descrição

Adiciona campo a campo as configurações de campos utilizado no Adapter

...

NomeTipoDescrição
cFieldJsonCarácterNome do campo no objeto Json
cFieldQueryCarácterNome do campo que será utilizado no ResultSet
lJsonFieldLógicoSe .T. informa que o campo será exportado ao Json
lFixedLógicoSe .T. informa que o campo não pode ser removido pelo FIELDS do QueryParam
aStructArrayVetor com a estrutura do campo no padrão {"CAMPO", "TIPO", Tamanho, Decimal}, caso não seja informada a estrutura, utiliza como base o dicionário SX3

...

cRenameField

Descrição
Informa a query para a geração do Json

Parâmetros

...

Deverá ser utilizado os Id's:
#QueryFields# Campos do SELECT, existe tratamento para o FIELDS no QueryParam
#QueryWhere# Condições do WHERE, existe tratamento para FILTER no QueryParam

SetWhere

Descrição

Faz o set da condição de filtro utilizada para ordenação do ResultSet, substitui o id #QueryWhere#

Parâmetros

...

SetOrder

Descrição

Faz o set da condição padrão utilizada para ordenação do ResultSet

Utilizada para definir a ordem padrão retornada caso não seja informada nenhum SetOrderQuery

Parâmetros

...

SetOrderQuery

Descrição

String contendo a ordenação informada via QueryParam

Substitui o cOrder padrão definida pelo método SetOrder

Parâmetros

...

Execute

Descrição

Realiza o parse dos ids #QueryFields# e #QueryWhere# gerando o ResultSet

Parâmetros

...

CarácterDetermina o nome real do campo na tabela, para o caso de identificadores ambíguos na query



Aviso
titleparâmetro cRenameField

O parâmetro cRenameField só está disponível em libs com label a partir de 20200727.

Bloco de código
languagetext
titlecampo com alias
collapsetrue
	// O próximo campo é um exemplo da possibilidade de renomear um campo ambíguo, contudo não é recomendado pois o recno pode mudar.
	// Quando uma tabela sofre backup e é restaurada com append existe a chance do recno ser reconstruído.
	// Portanto é melhor não oferecer a chave por PK igual ao recno e evitar que este problema ocorra.
	oSelf:AddMapFields( 'PK1'	, 'SB1RECNO' , .T., .F., { 'SB1RECNO', 'N', 15, 0 }, 'SB1.R_E_C_N_O_' )
	// Caso precise oferecer uma forma de recuperar o registro prefira a concatenação da chave do Protheus
	// essa expressão depende do banco de dados e não sofrerá parser pela changequery.
	oSelf:AddMapFields( 'PK2'	, 'SB1KEY' , .T., .F., { 'SB1KEY', 'C', 50, 0 }, 'B1_FILIAL+B1_COD' )

	// Aplicando em um outro campo
	oSelf:AddMapFields( 'NOTAS'	, 'MEUMEMO' , .T., .F., { 'B1_XMEMO', 'M', 10, 0 }, 'SB1.B1_XMEMO' )



...

SetQuery

Descrição
Informa a query para a geração do Json

...

FillGetResponse

Descrição

Método chamado linha a linha do ResultSet para geração do Json

SetPage

Descrição

Configura qual pagina sera retornada pelo adapter

Parâmetros

...

SetPageSize

Descrição

Configura o tamanho da pagina

Parâmetros

...

SetUrlFilter

Descrição

Faz o set do filtro informado via QueryParam


Parâmetros

NomeTipoDescrição
aUrlFilterArrayFiltro via QueryParam

GetJSONResponse

Descrição

Irá retornar o Json

Retorno Carácter, Resposta da API

Exemplo de utilização com a tabela de produtos:
Classe de criação do adapter
Bloco de código
#include 'totvs.ch'
#include 'parmtype.ch'
//-------------------------------------------------------------------
/*/{Protheus.doc} PrdAdapter
Classe Adapter para o serviço
@author  Anderson Toledo
/*/
//-------------------------------------------------------------------
CLASS PrdAdapter FROM FWAdapterBaseV2
	METHOD New()
	METHOD GetListProd()
EndClass

Method New( cVerb ) CLASS PrdAdapter
	_Super:New( cVerb, .T. )
return

Method GetListProd( ) CLASS PrdAdapter
	Local aArea 	AS ARRAY
	Local cWhere	AS CHAR
	aArea   := FwGetArea()
	//Adiciona o mapa de campos Json/ResultSet
	AddMapFields( self )
	//Informa a Query a ser utilizada pela API
	::SetQuery( GetQuery() )
	//Informa a clausula Where da Query
	cWhere := " B1_FILIAL = '"+ FWxFilial('SB1') +"' AND SB1.D_E_L_E_T_ = ' '"
	::SetWhere( cWhere )
	//Informa a ordenação padrão a ser Utilizada pela Query
	::SetOrder( "B1_COD" )
	//Executa a consulta, se retornar .T. tudo ocorreu conforme esperado
	If ::Execute() 
		// Gera o arquivo Json com o retorno da Query
		// Pode ser reescrita, iremos ver em outro artigo de como fazer
		::FillGetResponse()
	EndIf
	FwrestArea(aArea)
Return

Static Function AddMapFields( oSelf )
	
	oSelf:AddMapFields( 'CODE'              , 'B1_COD'  , .T., .T., { 'B1_COD', 'C', TamSX3( 'B1_COD' )[1], 0 } )
	oSelf:AddMapFields( 'DESCRIPTION'	    , 'B1_DESC' , .T., .F., { 'B1_DESC', 'C', TamSX3( 'B1_DESC' )[1], 0 } )	
	oSelf:AddMapFields( 'GROUP'		        , 'B1_GRUPO', .T., .F., { 'B1_GRUPO', 'C', TamSX3( 'B1_GRUPO' )[1], 0 } )
	oSelf:AddMapFields( 'GROUPDESCRIPTION'	, 'BM_DESC' , .T., .F., { 'BM_DESC', 'C', TamSX3( 'BM_DESC' )[1], 0 } )
Return 

Static Function GetQuery()
	Local cQuery AS CHARACTER
	
	//Obtem a ordem informada na requisição, a query exterior SEMPRE deve ter o id #QueryFields# ao invés dos campos fixos
	//necessáriamente não precisa ser uma subquery, desde que não contenha agregadores no retorno ( SUM, MAX... )
	//o id #QueryWhere# é onde será inserido o clausula Where informado no método SetWhere()
	cQuery := " SELECT #QueryFields#"
    cQuery +=   " FROM " + RetSqlName( 'SB1' ) + " SB1 "
    cQuery +=   " LEFT JOIN " + RetSqlName( 'SBM' ) + " SBM"
	cQuery +=       " ON B1_GRUPO = BM_GRUPO"
	cQuery +=           " AND BM_FILIAL = '"+ FWxFilial( 'SBM' ) +"'"
	cQuery +=           " AND SBM.D_E_L_E_T_ = ' '"
    cQuery += " WHERE #QueryWhere#"	
Return cQuery
Criação do serviço que irá executar o adapter
cQueryCarácterQuery para geração do Json

Deverá ser utilizado os Id's:
#QueryFields# Campos do SELECT, existe tratamento para o FIELDS no QueryParam
#QueryWhere# Condições do WHERE, existe tratamento para FILTER no QueryParam


...

SetWhere

Descrição

Faz o set da condição de filtro utilizada para ordenação do ResultSet, substitui o id #QueryWhere#


Parâmetros

NomeTipoDescrição
cWhereCarácterString contendo as condições do where para o ResultSet, substitui o id #QueryWhere#


...

SetOrder

Descrição

Faz o set da condição padrão utilizada para ordenação do ResultSet

Utilizada para definir a ordem padrão retornada caso não seja informada nenhum SetOrderQuery


Parâmetros

NomeTipoDescrição
cOrderCarácterString contendo a ordenação que irá ser utilizada no ResultSet


...

SetOrderQuery

Descrição

String contendo a ordenação informada via QueryParam

Substitui o cOrder padrão definida pelo método SetOrder


Parâmetros

NomeTipoDescrição
cOrderCarácterString contendo a ordenação que irá ser utilizada no ResultSet.
Obs.: Adicione "-" a esquera do cOrder para ordenar decrescente (ex.: "-filial").


...

SetFields

Descrição

Informa os campos que serão utilizados para geração do Json


Parâmetros

NomeTipoDescrição

cFields

Carácter

Campos que serão exportados para o Json, utilizado para setar os campos informados via QueryParam


...

Execute

Descrição

Realiza o parse dos ids #QueryFields# e #QueryWhere# gerando o ResultSet


Parâmetros

NomeTipoDescrição
lUseTmpTableLogicoForça o uso de tabela temporária, mesmo para os bancos com tratamento para geração de paginação via banco


Retorno Logico, Retorna se a execução foi realizada com sucesso


...

FillGetResponse

Descrição

Método chamado linha a linha do ResultSet para geração do Json


...

SetPage

Descrição

Configura qual pagina sera retornada pelo adapter


Parâmetros

NomeTipoDescrição
nPageNuméricoNúmero da pagina a ser retornada


...

SetPageSize

Descrição

Configura o tamanho da pagina


Parâmetros

NomeTipoDescrição
nPageSizeNuméricoTamanho da página


...

SetUrlFilter

Descrição

Faz a definição do filtro informado via parâmetros de query.


Parâmetros

NomeTipoDescrição
aUrlFilterArrayFiltro via QueryParam


O parâmetro aUrlFilter precisa seguir o formato do array da propriedade aQueryString do Rest Advpl.

No Rest Advpl este array é uma lista de outros array com duas posições, sendo a primeira posição a chave do parâmetro de query e a segunda posição o valor.

Bloco de código
languagecpp
titleexemplo aUrlFilter
collapsetrue
// filtros simples
// a requição com: ?propriedade1=valor1&propriedade2=valor2
// exigiria o array como
aUrlFilter := { ;
  {"propriedade1", "valor1"},;
  {"propriedade2", "valor2"} ;
}
self:SetUrlFilter(aUrlFilter)

// filtro complexos
// ?filter=propriedade1 eq 'valor1' and propriedade2 eq 'valor2'
aUrlFilter := { ;
  {"FILTER", "propriedade1 eq 'valor1' and propriedade2 eq 'valor2'"};
}
self:SetUrlFilter(aUrlFilter)

// Rest Advpl
// Essa versão do Rest já possui preparado um array com os parâmetros de query no formato adequado
// Com isso é possível indicar diretamento o atributo da classe aQueryString
self:SetUrlFilter(self:aQueryString)



...

SetStyleReturn

Descrição

Permite configurar o nome da propriedade de retorno dos itens da listagem do verbo GET.

Por padrão, o JSON retornado tem a propriedade items, esse método permite trocar o nome dessa propriedade.


Parâmetros

NomeTipoDescrição
cPropItemsCarácterNome da propriedade de retorno dos itens no JSON de listagem do verbo GET do adapter


Exemplo

oAdapter:SetStyleReturn("data")


Observação

Esse método está disponível somente para lib igual ou superior a 20221010


...

GetJSONResponse

Descrição

Irá retornar o Json

Retorno Carácter, Resposta da API


...

setIsCaseSensitive

Descrição

Indica que as propriedades do json são case sensitive.

Exemplo

oAdapter:setIsCaseSensitive(.T.)

Observação

Esse método está disponível somente para lib igual ou superior a 20230515


...

setUseSpaces

Descrição

Indica se considera os espaços à esquerda no retorno dos registros.

Exemplo

oAdapter:setUseSpaces(.T.)

Observação

Esse método está disponível somente para lib igual ou superior a 20230127


...

SetTlppFilter

Descrição

Definição de filtro para api tlpp onde os parâmetros recebidos vem no formato de json
Converte o objeto json em um array, e repassa para o metodo SetUrlFilter internamente

para ser usado em apis tlpp

Exemplo

jQueryParams := oRest:getQueryRequest()
oAdapter:SetTlppFilter(jQueryParams)

Observação

Esse método está disponível somente para lib igual ou superior a 20230515


...

Filtros complexos

Descrição

Filtros que são suportados pela classe, esses podem ser informados durante o consumo da API.

Lista

  • STARTSWITH
    • startswith(nome, 'JOAO')
  • ENDSWITH
    • endswith(nome, 'SILVA')
  • CONTAINS
    • contains(nome, 'SILVA')
  • TOUPPER
    • toupper(nome) eq 'JOAO DA SILVA'
  • TOLOWER
    • tolower(nome) eq 'joao da silva'

Exemplo genérico

  • contains(tolower(nome), 'joao') or endswith(cpf, '002')

Observação

Alguns filtros foram implementados em datas diferentes de outros, logo é bom verificar qual filtro está disponível conforma data de sua lib.

O campo data pode ter comportamentos diferentes por conta do dado ser gravado no banco de dados como character.


...

Exemplos

Exemplo de utilização com a tabela de produtos:
Classe de criação do adapter
Bloco de código
languagec#
themeRDark
titleAdapter
collapsetrue
#include 'totvs.ch'
#include 'parmtype.ch'
//-------------------------------------------------------------------
/*/{Protheus.doc} PrdAdapter
Classe Adapter para o serviço
@author  Anderson Toledo
/*/
//-------------------------------------------------------------------
CLASS PrdAdapter FROM FWAdapterBaseV2
	METHOD New()
	METHOD GetListProd()
EndClass

Method New( cVerb ) CLASS PrdAdapter
	_Super:New( cVerb, .T. )
return

Method GetListProd( ) CLASS PrdAdapter
	Local aArea 	AS ARRAY
	Local cWhere	AS CHAR
	aArea   := FwGetArea()
	//Adiciona o mapa de campos Json/ResultSet
	AddMapFields( self )
	//Informa a Query a ser utilizada pela API
	::SetQuery( GetQuery() )
	//Informa a clausula Where da Query
	cWhere := " B1_FILIAL = '"+ FWxFilial('SB1') +"' AND SB1.D_E_L_E_T_ = ' '"
	::SetWhere( cWhere )
	//Informa a ordenação padrão a ser Utilizada pela Query
	::SetOrder( "B1_COD" )
	//Executa a consulta, se retornar .T. tudo ocorreu conforme esperado
	If ::Execute() 
		// Gera o arquivo Json com o retorno da Query
		::FillGetResponse()
	EndIf
	FwrestArea(aArea)
Return

Static Function AddMapFields( oSelf )
	
	oSelf:AddMapFields( 'CODE'              , 'B1_COD'  , .T., .T., { 'B1_COD', 'C', TamSX3( 'B1_COD' )[1], 0 } )
	oSelf:AddMapFields( 'DESCRIPTION'	    , 'B1_DESC' , .T., .F., { 'B1_DESC', 'C', TamSX3( 'B1_DESC' )[1], 0 } )	
	oSelf:AddMapFields( 'GROUP'		        , 'B1_GRUPO', .T., .F., { 'B1_GRUPO', 'C', TamSX3( 'B1_GRUPO' )[1], 0 } )
	oSelf:AddMapFields( 'GROUPDESCRIPTION'	, 'BM_DESC' , .T., .F., { 'BM_DESC', 'C', TamSX3( 'BM_DESC' )[1], 0 } )
Return 

Static Function GetQuery()
	Local cQuery AS CHARACTER
	
	//Obtem a ordem informada na requisição, a query exterior SEMPRE deve ter o id #QueryFields# ao invés dos campos fixos
	//necessáriamente não precisa ser uma subquery, desde que não contenha agregadores no retorno ( SUM, MAX... )
	//o id #QueryWhere# é onde será inserido o clausula Where informado no método SetWhere()
	cQuery := " SELECT #QueryFields#"
    cQuery +=   " FROM " + RetSqlName( 'SB1' ) + " SB1 "
    cQuery +=   " LEFT JOIN " + RetSqlName( 'SBM' ) + " SBM"
	cQuery +=       " ON B1_GRUPO = BM_GRUPO"
	cQuery +=           " AND BM_FILIAL = '"+ FWxFilial( 'SBM' ) +"'"
	cQuery +=           " AND SBM.D_E_L_E_T_ = ' '"
    cQuery += " WHERE #QueryWhere#"	
Return cQuery
Criação do serviço que irá executar o adapter
Bloco de código
themeRDark
titleApi - advpl
collapsetrue
#include "totvs.ch"
#include "restful.ch"
//-------------------------------------------------------------------
/*/{Protheus.doc} products
Declaração do ws products
@author Anderson Toledo
/*/
//-------------------------------------------------------------------
WSRESTFUL products DESCRIPTION 'endpoint products API' FORMAT "application/json,text/html"
    WSDATA Page     AS INTEGER OPTIONAL
    WSDATA PageSize AS INTEGER OPTIONAL
    WSDATA Order    AS CHARACTER OPTIONAL
    WSDATA Fields   AS CHARACTER OPTIONAL

 	WSMETHOD GET ProdList;
	    DESCRIPTION "Retorna uma lista de produtos";
	    WSSYNTAX "/api/v1/products" ;
        PATH "/api/v1/products" ;
	    PRODUCES APPLICATION_JSON
 	
END WSRESTFUL

WSMETHOD GET ProdList QUERYPARAM Page WSREST products
Return getPrdList(self)

Static Function getPrdList( oWS )
   Local lRet  as logical
   Local oProd as object
   DEFAULT oWS:Page      := 1  
   DEFAULT oWS:PageSize  := 10
   DEFAULT oWS:Fields    := ""
   lRet        := .T.
   //PrdAdapter será nossa classe que implementa fornecer os dados para o WS
   // O primeiro parametro indica que iremos tratar o método GET
   oProd := PrdAdapter():new( 'GET' )  
   //o método setPage indica qual página deveremos retornar
   //ex.: nossa consulta tem como resultado 100 produtos, e retornamos sempre uma listagem de 10 itens por página.
   // a página 1 retorna os itens de 1 a 10
   // a página 2 retorna os itens de 11 a 20
   // e assim até chegar ao final de nossa listagem de 100 produtos 
   oProd:setPage(oWS:Page)
   // setPageSize indica que nossa página terá no máximo 10 itens
   oProd:setPageSize(oWS:PageSize)
   // SetOrderQuery indica a ordem definida por querystring
   oProd:SetOrderQuery(oWS:Order)
   // setUrlFilter indica o filtro querystring recebido (pode se utilizar um filtro oData)
   oProd:SetUrlFilter(oWS:aQueryString )
   // SetFields indica os campos que serão retornados via querystring
   oProd:SetFields( oWS:Fields )   
   // Esse método irá processar as informações
   oProd:GetListProd()
   //Se tudo ocorreu bem, retorna os dados via Json
   If oProd:lOk
       oWS:SetResponse(oProd:getJSONResponse())
   Else
   //Ou retorna o erro encontrado durante o processamento
       SetRestFault(oProd:GetCode(),oProd:GetMessage())
       lRet := .F.
   EndIf
   //faz a desalocação de objetos e arrays utilizados
   oProd:DeActivate()
   oProd := nil   
Return lRet
Bloco de código
languagejs
titleApi - tlpp
collapsetrue
#include "fw-tlpp-core.th"

//-------------------------------------------------------------------
/*/{Protheus.doc} zGroup
    API teste consulta Group
@author Caio Lima
@since 19/04/2023
//-----------------------------------------------------------------*/
Class zGroup
    public method new() as object

    @Get("/api/framework/zGroup")
    public method getZGroup() as Logical

    private Method callAdapter() as logical
   
EndClass

//-------------------------------------------------------------------
/*/{Protheus.doc} New
    Metodo construtor da classe
@return object, self
@author Caio Lima
@since 19/04/2023
//-----------------------------------------------------------------*/
Method new() as object Class zGroup
Return self

//-------------------------------------------------------------------
/*/{Protheus.doc} getZGroup
    api para listar usuários x funções x privilégios
@return logical, true caso deu certo a execução, do contrario retorna false
@author Caio Lima
@since 19/04/2023
//-----------------------------------------------------------------*/
Method getZGroup() as logical Class zGroup
Return(::callAdapter("Group", "displayName"))

//-------------------------------------------------------------------
/*/{Protheus.doc} callAdapter
    realiza as definições que são comuns para todos os adapters
@param cAdapter, adapter a ser chamado
@param cOrder, order default a ser utilizada caso não tenha sido definido na chamada
@return logical, true caso tenha dado tudo certo na requisição, do contrario retorna falso
@author Caio Lima
@since 19/04/2023
//-----------------------------------------------------------------*/
Method callAdapter(cAdapter as character, cOrder as character) as logical Class zGroup
    Local jQueryParams as json
    Local oAdapter as object
    Local lCalcTrueHasNext as logical
    Local lTOk as logical
    lTOk := .T.

    Default cOrder := "user_id"
    Default lIsUsrLogado := .F.
    lCalcTrueHasNext := .T.

    oRest:setKeyHeaderResponse("Content-Type", "application/json")

    jQueryParams := oRest:getQueryRequest()
    if cAdapter == "Group"
        oAdapter := totvs.framework.adapter.groups():new( 'GET' )
    EndIf

    if jQueryParams:HasProperty("pagesize")
        oAdapter:setPageSize(Val(jQueryParams["pagesize"]))
    Else
        oAdapter:setPageSize( 25 )
    endif

    if jQueryParams:HasProperty("order")
        oAdapter:SetOrderQuery(jQueryParams["order"])
    else
        oAdapter:SetOrderQuery(cOrder)
    endif

    if jQueryParams:HasProperty("page")
        oAdapter:setPage(Val(jQueryParams["page"]))
    Else
        oAdapter:setPage(1)
    endif

    oAdapter:SetQSearchPar()
    oAdapter:SetTlppFilter(jQueryParams)

    oAdapter:oJsonObj:CalcTrueHasNext(lCalcTrueHasNext)
    
    If lTOk
        oAdapter:getList()

        If oAdapter:IsOk()
            oRest:setResponse( oAdapter:gtJsonObjResponse():ToJson() )
        Else
            lTOk := .F.
            setRestFault(oAdapter:getCode(),oAdapter:getMessage())
        EndIf
    EndIf

    oAdapter:deActivate()
    FwFreeObj(oAdapter)

Return(.T.)

Exemplo com utilização de r_e_c_n_o_ e substr, a utilização de funções SQL pode exigir a necessidade de fazer uma subquery.
Sub-query também é util em casos onde se deseja utilizar a mesma tabela mais de uma vez na query.
Bloco de código
languagejs
themeMidnight
titleAdapter - SED (com sub-query)
collapsetrue
#include "protheus.ch"

//-------------------------------------------------------------------
/*/{Protheus.doc} NaturezasAdapter
Classe Adapter para o serviço de Naturezas SED

@author Daniel Mendes
@version 1.0
@since 07/09/2023
/*/
//-------------------------------------------------------------------
class NaturezasAdapter from FWAdapterBaseV2
    private data lAllFields as logical

    public method new()
    public method setAllFields()
    public method getListNaturezas()
endclass

//-------------------------------------------------------------------
/*/{Protheus.doc} new
Construtor da classe

@return self, object, instância da classe

@type method
@author Daniel Mendes
@version 1.0
@since 07/09/2023
/*/
//-------------------------------------------------------------------
method new( cVerb as character) as object class NaturezasAdapter
_Super:New(cVerb, .T.)
self:lAllFields := .F.
return

//-------------------------------------------------------------------
/*/{Protheus.doc} setAllFields
Efetua o set para retornar os campos de função e recno

@return lAllFields, logical, indica o retorno dos campos de função SQL e recno

@type method
@author Daniel Mendes
@version 1.0
@since 07/09/2023
/*/
//-------------------------------------------------------------------
method setAllFields(lAllFields as logical) class NaturezasAdapter
self:lAllFields := lAllFields
return

//-------------------------------------------------------------------
/*/{Protheus.doc} getListNaturezas
Executa o adapter

@type method
@author Daniel Mendes
@version 1.0
@since 07/09/2023
/*/
//-------------------------------------------------------------------
method getListNaturezas() class NaturezasAdapter
local cWhere as character

AddMapFields(self, self:lAllFields)
::SetQuery(GetQuery(self:lAllFields))

cWhere := " SED.ED_FILIAL = '"+ FWxFilial("SED") +"' AND SED.D_E_L_E_T_ = ' '"

::SetWhere( cWhere )
::SetOrder( "ED_CODIGO" )

::setIsCaseSensitive(.T.) //Mantém o case no JSON de resposta

if ::Execute()
    ::FillGetResponse()
endif

return

//-------------------------------------------------------------------
/*/{Protheus.doc} addMapFields
Adiciona os campos do adapter

@param oSelf, object, objeto do adapter
@param lAllFields, logical, indica o retorno dos campos de função SQL e recno

@type function
@author Daniel Mendes
@version 1.0
@since 07/09/2023
/*/
//-------------------------------------------------------------------
static function addMapFields(oSelf as object, lAllFields as logical)
oSelf:AddMapFields("code"              , "ED_CODIGO"  , .T., .T., { "ED_CODIGO", "C", TamSX3("ED_CODIGO")[1], 0 } )
oSelf:AddMapFields("description"       , "ED_DESCRIC" , .T., .F., { "ED_DESCRIC", "C", TamSX3("ED_DESCRIC")[1], 0 } )

if lAllFields
    oSelf:AddMapFields("recnoNickname"     , "RECNO_NICK"  , .T., .F., { "RECNO_NICK", "N", 16, 0 }) //Com "apelido" no campo
    oSelf:AddMapFields("recno"             , "RECNO"       , .T., .F., { "RECNO", "N", 16, 0 }, "R_E_C_N_O_") //Com campo especial
    oSelf:AddMapFields("subStrDescription" , "SUBSTR_DESC" , .T., .F., { "SUBSTR_DESC", "C", 5, 0 } ) //Com função SQL
endif

return

//-------------------------------------------------------------------
/*/{Protheus.doc} getQuery
Retorna a query do Adapter

@param lAllFields, logical, indica o retorno dos campos de função SQL e recno

@return cQuery, character, Query da SED

@type function
@author Daniel Mendes
@version 1.0
@since 07/09/2023
/*/
//-------------------------------------------------------------------
static function getQuery(lAllFields as logical) as character
local cQuery as character
local cTable as character

cTable := RetSqlName("SED")

cQuery := " SELECT #QueryFields#"
cQuery += " FROM "

if lAllFields
    cQuery += " ( SELECT ED_FILIAL, ED_CODIGO, ED_DESCRIC, R_E_C_N_O_ RECNO_NICK, "
    cQuery += " SUBSTR(ED_DESCRIC, 1 , 5) SUBSTR_DESC, R_E_C_N_O_, D_E_L_E_T_ "
    cQuery += " FROM " + cTable + " ) "
else
    cQuery += cTable
endif

cQuery += " SED "
cQuery += " WHERE #QueryWhere#"

return cQuery

Bloco de código
languagejs
themeMidnight
titleServiço - SED
collapsetrue
#include "protheus.ch"
#include "restful.ch"

//-------------------------------------------------------------------
/*/{Protheus.doc} naturezas
Serviço REST de Naturezas com Adapter

@author Daniel Mendes
@version 1.0
@since 07/09/2023
/*/
//-------------------------------------------------------------------
wsrestful naturezas description 'endpoint naturezas API' format "application/json,text/html"
    wsdata Page     as integer optional
    wsdata PageSize as integer optional
    wsdata Order    as character optional
    wsdata Fields   as character optional
 
    wsmethod get NatV1List;
        description "Retorna uma lista de naturezas";
        wssyntax "/api/v1/naturezas" ;
        path "/api/v1/naturezas" ;
        produces APPLICATION_JSON

    wsmethod get NatV2List;
        description "Retorna uma lista de naturezas";
        wssyntax "/api/v2/naturezas" ;
        path "/api/v2/naturezas" ;
        produces APPLICATION_JSON
     
end wsrestful
Bloco de código
#include "totvs.ch"
#include "restful.ch"
//-------------------------------------------------------------------
/*/{Protheus.doc} products
Declaração do ws products
@author Anderson Toledo
/*/
//-------------------------- NatV1List
Verbo GET da API de Naturezas

@return logical, indica sucesso na requisição

@type method
@author Daniel Mendes
@version 1.0
@since 07/09/2023
/*/
//-----------------------------------------
WSRESTFUL products DESCRIPTION 'endpoint products API' FORMAT "application/json,text/html"
    WSDATA Page     AS INTEGER OPTIONAL
    WSDATA PageSize AS INTEGER OPTIONAL
    WSDATA Order    AS CHARACTER OPTIONAL

 	WSMETHOD GET ProdList;
	    DESCRIPTION "Retorna uma lista de produtos";
	    WSSYNTAX "/api/v1/products" ;
        PATH "/api/v1/products" ;
	    PRODUCES APPLICATION_JSON
 	
END WSRESTFUL

WSMETHOD GET ProdList QUERYPARAM Page WSREST products
Return getPrdList(self)

Static Function getPrdList( oWS )
   Local lRet  as logical
   Local oProd as object
   DEFAULT oWS:Page      := 1  
   DEFAULT oWS:PageSize  := 10
   lRet        := .T.
   //PrdAdapter será nossa classe que implementa fornecer os dados para o WS
   // O primeiro parametro indica que iremos tratar o método GET
   oProd := PrdAdapter():new( 'GET' )  
   //o método setPage indica qual página deveremos retornar
   //ex.: nossa consulta tem como resultado 100 produtos, e retornamos sempre uma listagem de 10 itens por página.
   // a página 1 retorna os itens de 1 a 10
   // a página 2 retorna os itens de 11 a 20
   // e assim até chegar ao final de nossa listagem de 100 produtos 
   oProd:setPage(oWS:Page)
   // setPageSize indica que nossa página terá no máximo 10 itens
   oProd:setPageSize(oWS:PageSize)
   // SetOrderQuery indica a ordem definida por querystring
   oProd:SetOrderQuery(oWS:Order)
   // setUrlFilter indica o filtro querystring recebido (pode se utilizar um filtro oData)
   oProd:SetUrlFilter(oWS:aQueryString )
   // Esse método irá processar as informações
   oProd:GetListProd()
   //Se tudo ocorreu bem, retorna os dados via Json
   If oProd:lOk
       oWS:SetResponse(oProd:getJSONResponse())
   Else
   //Ou retorna o erro encontrado durante o processamento
       SetRestFault(oProd:GetCode(),oProd:GetMessage())
       lRet := .F.
   EndIf
   //faz a desalocação de objetos e arrays utilizados
   oProd:DeActivate()
   oProd := nil   
Return lRet

A partir do exemplo acima é possível realizar filtros no retorno do GET, abaixo exemplos utilizando paginação e o padrão oData.

Obs. Endereço e conteúdo da comparação deve ser ajustado de acordo com o ambiente utilizado.
Filtro utilizando paginação
Bloco de código
http://localhost:8080/teste/rest/api/v1/products?pagesize=1&page=3
Filtro utilizando o padrão oData
-----------------------------
wsmethod get NatV1List queryparam Page wsrest naturezas
return getNaturezasList(self, .F.)

//-------------------------------------------------------------------
/*/{Protheus.doc} NatV2List
Verbo GET da API de Naturezas

@return logical, indica sucesso na requisição

@type method
@author Daniel Mendes
@version 2.0
@since 07/09/2023
/*/
//-------------------------------------------------------------------
wsmethod get NatV2List queryparam Page wsrest naturezas
return getNaturezasList(self, .T.)

//-------------------------------------------------------------------
/*/{Protheus.doc} getNaturezasList
Executa o adapter de Naturezas para o verbo GET

@param oWS, object, objeto REST
@param lAllFields, logical, indica o retorno dos campos de função SQL e recno

@return lRet, logical, indica sucesso na requisição

@type function
@author Daniel Mendes
@version 1.0
@since 07/09/2023
/*/
//-------------------------------------------------------------------
static function getNaturezasList(oWS as object, lAllFields as logical)
local lRet  as logical
local oNatSED as object

default oWS:Page      := 1 
default oWS:PageSize  := 10
default oWS:Fields    := ""

oNatSED := NaturezasAdapter():new("GET")

oNatSED:setAllFields(lAllFields)
oNatSED:setPage(oWS:Page)
oNatSED:setPageSize(oWS:PageSize)
oNatSED:setOrderQuery(oWS:Order)
oNatSED:setUrlFilter(oWS:aQueryString)
oNatSED:setFields(oWS:Fields)
oNatSED:getListNaturezas()

lRet := oNatSED:IsOk()

if lRet
    oWS:SetResponse(oNatSED:getJSONResponse())
else
    SetRestFault(oNatSED:GetCode(),oNatSED:GetMessage())
EndIf

oNatSED:DeActivate()
FreeObj(oNatSED)
oNatSED := nil

return lRet

A partir do exemplo acima é possível realizar filtros no retorno do GET, abaixo exemplos utilizando paginação e o padrão oData.

Obs. Endereço e conteúdo da comparação deve ser ajustado de acordo com o ambiente utilizado.
Filtro utilizando paginação
Bloco de código
http://localhost:8080/teste/rest/api/v1/products?pagesize=1&page=3
Filtro utilizando o padrão oData
Bloco de código
http://localhost:8080/teste/rest/api/v1/products?filter=code eq '000001'
Filtro utilizando ordenação
Bloco de código
http://localhost:8080/teste/rest/api/v1/products?order=description
Filtro informando os campos que serão retornados
Bloco de código
http://localhost:8080/teste/rest/api/v1/products?fields=code,description
Informações
titleFeature

A partir da lib 20220322 é possível utilizar os filtros toupper e tolower:

filter=toupper(description) eq 'XISTO'

filter=tolower(description) eq 'xis'




A partir da lib 20220502 o JSON de retorno do GET com listagem, além de possuir a propriedade hasNext indicando que ainda existem registros a serem retornados, contará também com a propriedade remainingRecords, que retornará quantos registros ainda existem para retorno, facilitando assim por exemplo o cálculo da quantidade de páginas existentes.

Aviso
titleCampos memo - SYP

A classe não dá suporte ao campos memo que utilizam da SYP.

Nota
titleChangeQuery

A classe FWAdapterBaseV2 não faz uso da ChangeQuery, logo a utilização de concatenação e/ou funções SQL devem ser evitadas ou tratadas quando necessário.

Bloco de código
http://localhost:8080/teste/rest/api/v1/products?filter=code eq '000001'