Páginas filhas
  • FWRestModel - API RESTful dos modelos de dados do Protheus

Versões comparadas

Chave

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

Objetivo

 


Possibilitar acesso aos modelos de dados do Protheus através de API RESTFul.

Pré-requisitos


  • Conhecimento do MVC do Protheus
  • Configurar o servidor REST do TOTVS | Application Server


Requisitos Mínimos


Para utilizar certifique-se que:

  • O TOTVS | AppServer é superior ou igual a build 7.00.121227P - Aug 12 2013
  • O pacote de atualização da Lib do Microsiga Protheus aplicado no ambiente é superior a Junho/2015 (Lib 20150608)

* Para atualização do ambiente, consulte nossa Central de Download no endereço: http://www.totvs.com.br/suporte

 


Como utilizar

 


Os modelos de dados devem ser publicados para serem acessíveis através da API, para isso deve-se utilizar o seguinte comando:

 


Informações
PUBLISH MODEL REST NAME <nome> source <fonte> RESOURCE OBJECT <objeto>

Obs: Cada modelo deverá ser publicado utilizando esse comando. Não é necessário estar no mesmo fonte do modelo de dados.

 


NAME = Nome da API que representará o modelo de dados.

SOURCE = Fonte aonde está o modelo de dados (função modeldef), caso esse comando não for informado, será acatado o fonte atual. (Não é obrigatório)

RESOURCE OBJECT = Classe que deverá herdar da FwRestModel(Não é obrigatório) 


Com este comando os modelos de dados serão acessíveis através do seguinte endereço: <protocol>://<server>:<port>/fwmodel/<name of published model>/<PK>

Exemplo: http://localhost:8084/fwmodel/products 


Parametros 


<Name of published model> = Nome o modelo informado no comando

<PK> = Valor da chave primaria do alias do modelo em encodado em base64 (Opcional)

 


Classe FWRestModel (link:   FWRestModelObject.prx)

Bloco de código
languageruby
titleFWRestModelObject.prx
linenumberstrue
collapsetrue
#INCLUDE "TOTVS.CH"
#INCLUDE "FWMVCDEF.CH"
#INCLUDE "FILTEREX.CH"
// Função Dummy
Function __FwRestModel__()
Return 
//-------------------------------------------------------------------
/*/{Protheus.doc} FwRestModel
Classe base para controlar o acesso aos registros relacionados ao 
modelo de dados.
@author Felipe Bonvicini Conti
@since 25/06/2015
@version P11, P12
/*/
//-------------------------------------------------------------------
Class FwRestModel
	Data cName
	Data cAlias
	Data cQryAlias
	Data aFields
	Data oModel
	Data lXml
	Data lActivate
	Data cFilter
	Data cOldFilter
	Data nStatus
	Data cStatus
	Data aQueryString
	Data lDebug
	Method Activate()
	Method DeActivate()
	Method OnError()
	Method SetModel()
	Method ClearModel()
	Method SetName()
	Method GetName()
	Method SetAsXml()
	Method SetAsJson()
	Method StartGetFormat()
	Method EscapeGetFormat()
	Method EndGetFormat()
	Method SetAlias()
	Method GetAlias()
	Method HasAlias()
	Method Seek()
	Method Skip()
	Method Total()
	Method GetData()
	Method SaveData()
	Method DelData()
	Method SetFilter()
	Method GetFilter()
	Method ClearFilter()
	Method DecodePK()
	Method ConvertPK()
	Method GetStatusResponse()
	Method SetStatusResponse()
	Method SetQueryString()
	Method GetQueryString()
	Method GetQSValue()
	Method GetHttpHeader()
	Method SetFields()
	Method debuger()
EndClass
//-------------------------------------------------------------------
/*/{Protheus.doc} Activate
Método de ativação da classe.
@return lActivate		Indica que a classe foi ativada.
@author Felipe Bonvicini Conti
@since 25/06/2015
@version P11, P12
/*/
//-------------------------------------------------------------------
Method Activate() Class FwRestModel
Default self:lDebug := .F.
	self:lXml   := .T.
Return self:lActivate := .T.
//-------------------------------------------------------------------
/*/{Protheus.doc} DeActivate
Método de desativação da classe.
@author Felipe Bonvicini Conti
@since 25/06/2015
@version P11, P12
/*/
//-------------------------------------------------------------------
Method DeActivate() Class FwRestModel
	FwFreeObj(self:ClearModel(aQueryString)
	FwFreeObj(self:ClearFilter(aFields)
	self:lActivate := .F.
Return 
//-------------------------------------------------------------------
/*/{Protheus.doc} OnError
Método que será executado quando algum erro ocorrer no rest.
@author Felipe Bonvicini Conti
@since 25/06/2015
@version P11, P12
/*/
//-------------------------------------------------------------------
Method OnError() Class FwRestModel
	self:DeActivate()
	self:ClearModel()
Return 
//-------------------------------------------------------------------
/*/{Protheus.doc} SetModel
Método para setar o modelo de dados que será utilizado.
E força a configuração do alias a ser utilizado.
@param	oModel	Objeto do modelo de dados
@return	oModel	Objeto do modelo de dados setado.
@author Felipe Bonvicini Conti
@since 25/06/2015
@version P11, P12
/*/
//-------------------------------------------------------------------
Method SetModel(oModel) Class FwRestModel
	self:oModel := oModel
	self:SetAlias()
Return oModel
//-------------------------------------------------------------------
/*/{Protheus.doc} ClearModel
Método para efetuar o desroy do modelo de dados. 
@author Felipe Bonvicini Conti
@since 25/06/2015
@version P11, P12
/*/
//-------------------------------------------------------------------
Method ClearModel() Class FwRestModel
	If self:oModel != Nil
		self:oModel:Destroy()
		self:oModel := Nil
	EndIf
Return 
	self:ClearFilter()
Return 
//-------------------------------------------------------------------
/*/{Protheus.doc} SetName
Método para setar o nome do rest do modelo de dados. 
@return	cName	Nome do rest do modelo de dados.
@author Felipe Bonvicini Conti
@since 25/06/2015
@version P11, P12
/*/
//-------------------------------------------------------------------
Method SetName(cName) Class FwRestModel
Return self:cName := cName
//-------------------------------------------------------------------
/*/{Protheus.doc} GetName
Método para retornar o nome do rest do modelo de dados. 
@return	cName	Nome do rest do modelo de dados.
@author Felipe Bonvicini Conti
@since 25/06/2015
@version P11, P12
/*/
//-------------------------------------------------------------------
Method GetName() Class FwRestModel
Return self:cName
//-------------------------------------------------------------------
/*/{Protheus.doc} SetAsXml
Método para setar o retorno do rest como XML. 
@author Felipe Bonvicini Conti
@since 25/06/2015
@version P11, P12
/*/
//-------------------------------------------------------------------
Method SetAsXml() Class FwRestModel
	self:lXml := .T.
Return 
//-------------------------------------------------------------------
/*/{Protheus.doc} SetAsJson
Método para setar o retorno do rest como JSON. 
@author Felipe Bonvicini Conti
@since 25/06/2015
@version P11, P12
/*/
//-------------------------------------------------------------------
Method SetAsJson() Class FwRestModel
	self:lXml := .F.
Return 
//-------------------------------------------------------------------
/*/{Protheus.doc} StartGetFormat
Método retornar o conteúdo inicial do dado de retorno.
@param	nTotal			Quantidade total de registros do alias que podem ser retornados.
@param	nCount			Quantidade de registros a serem retornados.
@param	nStartIndex	Index inicial do registro que será retornado.
@return	cRet				Conteúdo inicial
@author Felipe Bonvicini Conti
@since 25/06/2015
@version P11, P12
/*/
//-------------------------------------------------------------------
Method StartGetFormat(nTotal, nCount, nStartIndex) Class FwRestModel
Local cRet := ""
	If self:lXml
		cRet += '<?xml version="1.0" encoding="UTF-8"?>'
		cRet += '<result>'
		cRet += i18n('<total>#1</total><count>#2</count><startindex>#3</startindex>', {nTotal, nCount, nStartIndex})
		cRet += '<resources>'
	Else
		cRet += i18n('{"total":#1,"count":#2,"startindex":#3,"resources":[', {nTotal, nCount, nStartIndex})
	EndIf
Return cRet
//-------------------------------------------------------------------
/*/{Protheus.doc} EscapeGetFormat
Método retornar o caracter a ser inserido entre os registros a serem 
retornados.
@return	cRet	Caracter de escape.
@author Felipe Bonvicini Conti
@since 25/06/2015
@version P11, P12
/*/
//-------------------------------------------------------------------
Method EscapeGetFormat() Class FwRestModel
Local cRet := ""
	If !self:lXml
		cRet := ","
	EndIf
Return cRet
//-------------------------------------------------------------------
/*/{Protheus.doc} EndGetFormat
Método retornar o conteúdo final do dado de retorno.
@return	cRet	Conteúdo final
@author Felipe Bonvicini Conti
@since 25/06/2015
@version P11, P12
/*/
//-------------------------------------------------------------------
Method EndGetFormat() Class FwRestModel
Local cRet := ""
	If self:lXml
		cRet := "</resources>"
		cRet += "</result>"
	Else
		cRet := "]}"
	EndIf
Return cRet
//-------------------------------------------------------------------
/*/{Protheus.doc} SetAlias
Método responsável por setar o alias a ser utilizado.
Se o mesmo não for informado, será utilizado o alias do field 
principal do modelo de dados.
@return	lRet	Indica se o alias foi preenchido
@author Felipe Bonvicini Conti
@since 25/06/2015
@version P11, P12
/*/
//-------------------------------------------------------------------
Method SetAlias(cAlias) Class FwRestModel
	If Empty(cAlias)
		If !Empty(self:oModel)
			self:cAlias := MPGetModelAlias(self:oModel)
			dbSelectArea(self:cAlias)
		EndIf
	Else
		self:cAlias := cAlias
	EndIf
Return !Empty(self:cAlias)
//-------------------------------------------------------------------
/*/{Protheus.doc} HasAliasGetAlias
Método responsável informarpor seretornar o alias foi setado, se não força efetuar
o setAlias().
@return	lRet	Indica se o alias foi preenchidocAlias	Alias
@author Felipe Bonvicini Conti
@since 2505/0604/20152016
@version P11, P12
/*/
//-------------------------------------------------------------------
Method HasAliasGetAlias() Class FwRestModel
Local lRet := .T.
	If Empty(self:cAlias)
		self:SetAlias()
		lRet := !Empty(Return self:cAlias)
	EndIf
Return lRet
//-------------------------------------------------------------------
/*/{Protheus.doc} SeekHasAlias
Método responsável informar buscarse umo registroalias emfoi específicosetado, nose alias selecionado.
Se o parametro cPK não for informaodo, indica que deve-se ser posicionado
no primeiro registro da tabela.
@param	cPK						PK do registro.
@param	lRefreshAlias		Informar se o seek efetuara um refresh no alias antes de buscarnão força efetuar
o setAlias().
@return	lRet	Indica se foio encontradoalias algumfoi registro.preenchido
@author Felipe Bonvicini Conti
@since 25/06/2015
@version P11, P12
/*/
//-------------------------------------------------------------------
Method SeekHasAlias(cPK, lRefreshAlias) Class FwRestModel
Local lRet := .F.
Local nOrder
Default lRefreshAlias := .T.
	If Empty(self:HasAlias()
		If lRefreshAlias
	cAlias)
		RefreshAlias(self:cAliasSetAlias()
		EndIf
		If Empty(cPK)
			(self:cAlias)->(DbGotop())
			lRet :lRet := !Empty(self:cAlias)->(Eof())
		Else
			nOrder := MPGetBestOrder(self:cAlias, self:oModel:GetPrimaryKey(), 1)
			(self:cAlias)->(dbSetOrder(nOrder))
			lRet := (self:cAlias)->(DbSeek(cPK))
		Endif
	Endif

	EndIf
Return lRet
//-------------------------------------------------------------------
/*/{Protheus.doc} SkipSeek
Método responsável passarbuscar paraum oregistro proximoem registroespecífico dono alias selecionado.
Se o parametro cPK não for informaodo, indica que deve-se ser posicionado
no primeiro registro da tabela.
@param	cPK						PK do registro.
@return	lRet	Indica se ofoi aliasencontrado não chegou ao fimalgum registro.
@author Felipe Bonvicini Conti
@since 25/06/2015
@version P11, P12
/*/
//-------------------------------------------------------------------
Method SkipSeek(cPK) Class FwRestModel
Local lRet := .F.
Local cQry
Local cPkFilter
	If self:HasAlias()
		(self:cAlias)->(DbSkip()If !Empty(cPK)
			lRetcPkFilter := !FWAToS(self:cAlias)->(Eof())
	EndIf
Return lRet
//-------------------------------------------------------------------
/*/{Protheus.doc} Total
Método responsável retornar a quantidade total de regitros do alias.
Contagem é feita atravez de query.
@return	nTotal	Quantidade total de registros.
@author Felipe Bonvicini Conti
@since 25/06/2015
@version P11, P12
/*/
//:oModel:GetPrimaryKey(),"||") + " = '" + cPk + "'"
			If (self:cAlias)->(FieldPos(PrefixoCpo(self:cAlias)+"_FILIAL")) > 0
				cPkFilter := PrefixoCpo(self:cAlias)+"_FILIAL||" + cPkFilter
			EndIf
			self:SetFilter(cPkFilter)
		Endif
		cQry := createQueryAlias(@self:cQryAlias, self:cAlias, self:cFilter)
		If self:lDebug .And. self:GetQSValue("showQuery") == "true"
			i18nConOut("[FWRESTMODELOBJECT] Query: #1#2", {CRLF, cQry})
		EndIf
		If !(self:cQryAlias)->(Eof())
			(self:cAlias)->(dbGoTo((self:cQryAlias)->R_E_C_N_O_))
			lRet := !(self:cAlias)->(Eof())
		EndIf
	Endif
Return lRet
//-------------------------------------------------------------------
Method Total() Class FwRestModel
Local nTotal := 0
	If self:HasAlias()
		nTotal := FWTblCount(self:cAlias,,.T.)
	EndIf
Return nTotal/*/{Protheus.doc} Skip
Método responsável passar para o proximo registro do alias.
@return	lRet	Indica se o alias não chegou ao fim.
@author Felipe Bonvicini Conti
@since 25/06/2015
@version P11, P12
/*/
//-------------------------------------------------------------------
/*/{Protheus.doc} GetData
Método responsável por retornar o registro do modelo no formato XML 
ou JSON.
@param	lFieldDetail	Indica se retorna o registro com informações detalhadas
@param	lFieldVirtual	Indica se retorna o registro com campos virtuais
@param	lFieldEmpty 	Indica se retorna o registro com campos nao obrigatorios vazios
@return	cRet					Retorna o registro nos formatos XML ou JSON
@author Felipe Bonvicini Conti
@since 25/06/2015
@version P11, P12
/*/
//Method Skip(nSkip) Class FwRestModel
Local lRet := .F.
	If self:HasAlias()
		If !(self:cQryAlias)->(Eof())
			(self:cQryAlias)->(DbSkip(nSkip))
			(self:cAlias)->(dbGoTo((self:cQryAlias)->R_E_C_N_O_))
			lRet := !(self:cAlias)->(Eof())
		EndIf
	EndIf
Return lRet
//--------------------------------------------------------------------
Method GetData(lFieldDetail, lFieldVirtual, lFieldEmpty) Class FwRestModel
Local cRet
	self:oModel:SetOperation(MODEL_OPERATION_VIEW)
	Self:oModel:Activate()
	If self:lXml
		cRet := Self:oModel:GetXmlData(lFieldDetail,,,lFieldVirtual,,lFieldEmpty,.F./*lDefinition*/,,.T./*lPK*/,.T./*lPKEncoded*/)
	Else
		cRet := Self:oModel:GetJsonData(lFieldDetail,,lFieldVirtual,,lFieldEmpty,.T./*lPK*/,.T./*lPKEncoded*/)
	EndIf
	Self:oModel:DeActivate()
Return cRet

/*/{Protheus.doc} Total
Método responsável retornar a quantidade total de regitros do alias.
Contagem é feita atravez de query.
@return	nTotal	Quantidade total de registros.
@author Felipe Bonvicini Conti
@since 25/06/2015
@version P11, P12
/*/
//-------------------------------------------------------------------
/*/{Protheus.doc} SaveData
Método responsável por salvar o registro recebido pelo metodo PUT ou POST.
Se o parametro cPK não for informado, significa que é um POST.
@param	cPK			PK do registro.
@param	cData		Conteúdo a ser salvo
@param	@cError	Retorna o alguma mensagem de erro
@return	lRet		Indica se o registro foi salvo
@author Felipe Bonvicini Conti
@since 25/06/2015
@version P11, P12
/*/Method Total() Class FwRestModel
Local nTotal     := 0
Local cQueryPart := ""
	If self:HasAlias()
		cQueryPart += GetFromQryAlias(self:cAlias)
		cQueryPart += GetWhereQryAlias(self:cAlias, self:cFilter)
		nTotal := FWTblCount(self:cQryAlias, cQueryPart, .F.)
	EndIf
Return nTotal
//-------------------------------------------------------------------
Method SaveData(cPK, cData, cError) Class FwRestModel
local lRet := .T.
Default cData	:= ""
	If Empty(cPk)
		self:oModel:SetOperation(MODEL_OPERATION_INSERT)
	Else
		self:oModel:SetOperation(MODEL_OPERATION_UPDATE)
		lRet := self:Seek(cPK)
	EndIf
	If lRet
		self:oModel:Activate()
		If self:lXml
			lRet := self:oModel:LoadXMLData(cData)
		Else
			lRet := self:oModel:LoadJsonData(cData)
		EndIf
		If lRet
			If self:oModel:lModify // Verifico se o modelo sofreu alguma alteração
				If !(self:oModel:VldData() .And. self:oModel:CommitData())
					lRet := .F.
					cError := ErrorMessage(self:oModel:GetErrorMessage())
				EndIf
			Else
				lRet := .F.
				self:SetStatusResponse(403, "Not Modified")
			EndIf
		Else
			cError := ErrorMessage(self:oModel:GetErrorMessage())
		EndIf
		Self:oModel:DeActivate()
	Else
		cError := i18n("Invalid record '#1' on table #2", {cPK, self:cAlias})
	EndIf
Return lRet
//-/*/{Protheus.doc} GetData
Método responsável por retornar o registro do modelo no formato XML 
ou JSON.
@param	lFieldDetail	Indica se retorna o registro com informações detalhadas
@param	lFieldVirtual	Indica se retorna o registro com campos virtuais
@param	lFieldEmpty 	Indica se retorna o registro com campos nao obrigatorios vazios
@param	lFirstLevel		Indica se deve retornar todos os modelos filhos ou nao
@param	lInternalID     Indica se deve retornar o ID como informação complementar das linhas do GRID 
@return	cRet		Retorna o registro nos formatos XML ou JSON
@author Felipe Bonvicini Conti
@since 25/06/2015
@version P11, P12
/*/
//-------------------------------------------------------------------
/*/{Protheus.doc} DelData
Método responsável por remover um registro.
@param	cPK			PK do registro.
@param	@cError	Retorna o alguma mensagem de erro
@return	lRet		Indica se o registro foi removido
@author Felipe Bonvicini Conti
@since 25/06/2015
@version P11, P12
/*/
//----------------Method GetData(lFieldDetail, lFieldVirtual, lFieldEmpty, lFirstLevel, lInternalID) Class FwRestModel
Local cRet
	self:oModel:SetOperation(MODEL_OPERATION_VIEW)
	Self:oModel:Activate()
	If self:lXml
		cRet := Self:oModel:GetXmlData(lFieldDetail,,,lFieldVirtual,,lFieldEmpty,.F./*lDefinition*/,,.T./*lPK*/,.T./*lPKEncoded*/,self:aFields,lFirstLevel,lInternalID)
	Else
		cRet := Self:oModel:GetJsonData(lFieldDetail,,lFieldVirtual,,lFieldEmpty,.T./*lPK*/,.T./*lPKEncoded*/,self:aFields,lFirstLevel,lInternalID)
	EndIf
	Self:oModel:DeActivate()
Return cRet
//-------------------------------------------------------
Method DelData(cPK, cError) Class FwRestModel
local lRet := .F.
	If !Empty(cPK)
		If self:Seek(cPK)
			self:oModel:SetOperation(MODEL_OPERATION_DELETE)
			self:oModel:Activate()
			lRet := self:oModel:VldData() .And. self:oModel:CommitData()
			If !lRet
				cError := ErrorMessage(self:oModel:GetErrorMessage())
			EndIf
			Self:oModel:DeActivate()
		Else
			cError := i18n("Invalid record '#1' on table #2", {cPK, self:cAlias})
		EndIf
	EndIf
Return lRet
------------
/*/{Protheus.doc} SaveData
Método responsável por salvar o registro recebido pelo metodo PUT ou POST.
Se o parametro cPK não for informado, significa que é um POST.
@param	cPK			PK do registro.
@param	cData		Conteúdo a ser salvo
@param	@cError	Retorna o alguma mensagem de erro
@return	lRet		Indica se o registro foi salvo
@author Felipe Bonvicini Conti
@since 25/06/2015
@version P11, P12
/*/
//-------------------------------------------------------------------
/*/{Protheus.doc} SetFilter
Método responsável por setar algum filtro que tenha sido informado 
por Query String no REST.
@param	cFilter	Valor do filtro a ser aplicado no alias
@return	lRet		Indica se o filtro foi aplicado corretamente
@author Felipe Bonvicini Conti
@since 25/06/2015
@version P11, P12
/*/
//-------------------------------------------------------------------
Method SetFilter(cFilter) Class FwRestModel
Local lRet := .F.
	If !Empty(cFilter)
		self:cFilter := Alltrim(cFilter)
		If self:HasAlias()
			self:cOldFilter := (self:cAlias)->(dbFilterMethod SaveData(cPK, cData, cError) Class FwRestModel
local lRet := .T.
Default cData	:= ""
	If Empty(cPk)
		self:oModel:SetOperation(MODEL_OPERATION_INSERT)
	Else
		self:oModel:SetOperation(MODEL_OPERATION_UPDATE)
		lRet := self:Seek(cPK)
	EndIf
	If lRet
		self:oModel:Activate()
		If self:lXml
			lRet := self:oModel:LoadXMLData(cData)
		Else
			lRet := self:oModel:LoadJsonData(cData)
		EndIf
		If lRet
			If self:oModel:lModify // Verifico se o modelo sofreu alguma alteração
				If !(self:oModel:VldData() .And. self:oModel:CommitData())
			If !Empty(self:cOldFilter)
		lRet := .F.
					cError := ErrorMessage(self:cAlias)->(dbClearFilter(:oModel:GetErrorMessage())
				EndIf
			(self:cAlias)->(dbSetFilter(&('{|| '+self:cFilter+'}'), self:cFilter))
Else
				lRet := .TF.
				EndIf
	EndIf
Return self:SetStatusResponse(304, "Not Modified")
			EndIf
		Else
			cError := ErrorMessage(self:oModel:GetErrorMessage())
		EndIf
		Self:oModel:DeActivate()
	Else
		cError := i18n("Invalid record '#1' on table #2", {cPK, self:cAlias})
	EndIf
Return lRet
//-------------------------------------------------------------------
/*/{Protheus.doc} GetFilterDelData
Método responsável parapor retornarremover o conteúdoum registro.
@param	cPK			PK do filtroregistro.
@param	@cError	Retorna o alguma mensagem de erro
@return	cFilterlRet		Conteúdo do filtroIndica se o registro foi removido
@author Felipe Bonvicini Conti
@since 25/06/2015
@version P11, P12
/*/
//-------------------------------------------------------------------
Method GetFilterDelData(cPK, cError) Class FwRestModel
Returnlocal lRet self:cFilter
//-------------------------------------------------------------------
/*/{Protheus.doc} GetFilter
Método responsável pro limpar o filtro setado.
@author Felipe Bonvicini Conti
@since 25/06/2015
@version P11, P12
/*/
//-------------------------------------------------------------------
Method ClearFilter() Class FwRestModel
	(self:cAlias)->(dbClearFilter())
	If !Empty(self:cOldFilter)
		(self:cAlias)->(dbSetFilter(&('{|| '+AllTrim(self:cOldFilter)+'}'), self:cOldFilter))
		self:cOldFilter := Nil
	EndIf
Return := .F.
	If !Empty(cPK)
		If self:Seek(cPK)
			self:oModel:SetOperation(MODEL_OPERATION_DELETE)
			self:oModel:Activate()
			lRet := self:oModel:VldData() .And. self:oModel:CommitData()
			If !lRet
				cError := ErrorMessage(self:oModel:GetErrorMessage())
			EndIf
			Self:oModel:DeActivate()
		Else
			cError := i18n("Invalid record '#1' on table #2", {cPK, self:cAlias})
		EndIf
	EndIf
Return lRet
//-------------------------------------------------------------------
/*/{Protheus.doc} DecodePKSetFilter
Método pararesponsável indicarpor sesetar devealgum serfiltro feitoque o decode do paramtro cPK recebido 
no RESTtenha sido informado 
por Query String no REST.
@param	cFilter	Valor do filtro a ser aplicado no alias
@return	lRet		Indica se o filtro foi aplicado corretamente
@author Felipe Bonvicini Conti
@since 25/06/2015
@version P11, P12
/*/
//-------------------------------------------------------------------
Method DecodePKSetFilter(cFilter) Class FwRestModel
	self:cFilter := Alltrim(cFilter)
Return .T.
//-------------------------------------------------------------------
/*/{Protheus.doc} ConvertPKGetFilter
Método responsávelpara retornar poro converterconteúdo ado PKfiltro.
@return	cFilter		Conteúdo do filtro
@author Felipe Bonvicini Conti
@since 25/06/2015
@version P11, P12
/*/
//-------------------------------------------------------------------
Method ConvertPKGetFilter(cPK) Class FwRestModel
	IfReturn self:DecodePK()
		cPK := Decode64(cPK)
	EndIf
Return cPK
cFilter
//-------------------------------------------------------------------
/*/{Protheus.doc} GetStatusResponseGetFilter
Método responsável porpro retornarlimpar o codigo e descrição do status de retorno, 
quando necessario.filtro setado.
@author Felipe Bonvicini Conti
@since 0725/0706/2015
@version P11, P12
/*/
//-------------------------------------------------------------------
Method GetStatusResponseClearFilter() Class FwRestModel
Local Ret
	If !(self:nStatus == Nil)
		Ret cFilter := {self:nStatus, self:cStatus}
	EndIf""
Return Ret.T.
//-------------------------------------------------------------------
/*/{Protheus.doc} SetStatusResponseDecodePK
Método para responsávelindicar porse setardeve oser codigofeito eo descriçãodecode do statusparamtro decPK retorno,recebido 
quandono necessario.REST
@author Felipe Bonvicini Conti
@since 0725/0706/2015
@version P11, P12
/*/
//-------------------------------------------------------------------
Method SetStatusResponseDecodePK(nStatus, cStatus) Class FwRestModel
	self:nStatus := nStatus
	self:cStatus := cStatus
Return 
// ********************* Functions *********************
Static Function ErrorMessage(aErroMsg)
Local cRet := CRLF + " --- Error on Model ---" + CRLF
	cRet += "Id submodel origin: [" + aErroMsg[1] + "]" + CRLF
	cRet += "Id field origin: [" + aErroMsg[2] + "]" + CRLF
	cRet += "Id submodel error: [" + aErroMsg[3] + "]" + CRLF
	cRet += "Id field error: [" + aErroMsg[4] + "]" + CRLF
	cRet += "Id error: [" + aErroMsg[5] + "]" + CRLF
	cRet += "Error menssage: [" + aErroMsg[6] + "]" + CRLF
	cRet += "Solution menssage: [" + aErroMsg[7] + "]" + CRLF
	cRet += "Assigned value: [" + cValToChar( aErroMsg[8] ) + "]" + CRLF
	cRet += "Previous value: [" + cValToChar( aErroMsg[9] ) + "]" + CRLF
	aErroMsg := aSize(aErroMsg, 0)
Return cRet
//--------.T.
//-------------------------------------------------------------------
/*/{Protheus.doc} ConvertPK
Método responsável por converter a PK.
@author Felipe Bonvicini Conti
@since 25/06/2015
@version P11, P12
/*/
//-------------------------------------------------------------------
Method ConvertPK(cPK) Class FwRestModel
	If self:DecodePK()
		cPK := Decode64(cPK)
	EndIf
Return cPK
//-------------------------------------------------------------------
/*/{Protheus.doc} GetStatusResponse
Método responsável por retornar o codigo e descrição do status de retorno, 
quando necessario.
@author Felipe Bonvicini Conti
@since 07/07/2015
@version P11, P12
/*/
//-------------------------------------------------------------------
Method GetStatusResponse() Class FwRestModel
Local Ret
	If !(self:nStatus == Nil)
		Ret := {self:nStatus, self:cStatus}
	EndIf
Return Ret
//-------------------------------------------------------------------
/*/{Protheus.doc} RefreshAliasSetStatusResponse
Método responsável por atualizarsetar o Alias.
@param	cAlias		Alias que deve ser atualizadocodigo e descrição do status de retorno, 
quando necessario.
@author Felipe Bonvicini Conti
@since 3007/07/2015
@version P11, P12
/*/
//-------------------------------------------------------------------
StaticMethod Function RefreshAlias(cAlias)
	If Select(cAlias) > 0
		(cAlias)->(dbCloseArea())
	EndIf
	dbSelectArea(cAlias)
Return

 

Utilização

Esta API segue o padrão de REST.

Por exemplo:

Para retornar a lista de registros referente ao modelo de dados deve-se efetuar um GET sem informar a <PK>.

Para inserir um registro deve-se efetuar um POST sem informar a <PK> e enviar no body o conteúdo a ser inserido.

Ao informar o parâmetro <PK> será acessado um registro em específico e assim podendo ser utilizado os métodos GET, PUT, DELETE.

 

QueryStrings

 

COUNT = Quantidade de registro que devem ser retornados

STARTINDEX = Indica a partir que qual index deverá ser retornado

FILTER = Filtro que será aplicado no método SetFilter()

FIELDDETAIL = Habilita mostrar mais informações nos campos do modelo

FIELDVIRTUAL = Habilita o retorno de campos virtuais

Exemplo de utilização

 

Ao publicar um modelo de dados, se o mesmo utilizar alias, basta publicá-lo.

Caso o modelo tenha sido desenvolvido utilizando array ou carga manual, deve-se criar uma classe e herda-la da FwRestModel e sobrescrever os métodos necessários para atender a sua necessidade.

 

Exemplo de modelo de dados com customização da classe REST:

Bloco de código
languageruby
titleModelo de dados customizando a classe REST
linenumberstrue
#INCLUDE "TOTVS.CH"
#INCLUDE "FWMVCDEF.CH"
 
PUBLISH MODEL REST NAME Branch RESOURCE OBJECT oRestBranch
 
Class oRestBranch From FwRestModel
	Data lSm0Closed
	Method Activate()
	Method DeActivate()
	Method Total()
	Method SetAlias()
EndClass

Method Activate() Class oRestBranch
	self:lSm0Closed := .F.
	If Select("SM0") == 0
		self:lSm0Closed := .T.
		OpenSm0(, .F.)
	EndIf
Return _Super:Activate()

Method DeActivate() Class oRestBranch
	If self:lSm0Closed
		SM0->(dbCloseArea())
	EndIf
Return _Super:DeActivate()

Method Total() Class oRestBranch
Local nRecno := SM0->(Recno())
Local nTotal := 0
	If self:Seek()
		While !SM0->(Eof())
			nTotal++
			self:Skip()
		End
	EndIf
	SM0->(dbGoTo(nRecno))
Return nTotal

Method SetAlias() Class oRestBranch
	self:cAlias := "SM0"
Return .T.

// MODELO DE DADOS

Static Function Modeldef()
Local oStruSM0 := DefStrModel()
	oModel := FWFormModel():New( 'MYFILIAL', {|| }, {|| }, {|| }, {|| } )
	oModel:AddFields( 'SM0MASTER', , oStruSM0, {|| }, {|| },{|oM| MyLoad() })
	oModel:SetDescription( "Empresas Protheus" )
	oModel:GetModel( 'SM0MASTER' ):SetDescription( "Empresas Protheus" )
	oModel:SetPrimaryKey( {"M0_CODIGO", "M0_CODFIL"} ) 
Return oModel
 
Static Function DefStrModel()
Local oStruct  := FWFormModelStruct():New()
Local bValid   := { || .T.}
Local bWhen    := { || }
Local bRelac   := { || }
	
		// TABELA
		oStruct:AddTable( "SM0", {}, "Filiais", {|| })                 
		
		// INDICES
		oStruct:AddIndex(1, "1", "M0_CODIGO", "Cód Empresa", "", "", .T.)
	
		// CAMPOS
		oStruct:AddField( "Cód Empresa"   , "Cód Empresa"    , "M0_CODIGO" , "C", 50, 0, bValid, bWhen, , , bRelac, .F., , , )                               
		oStruct:AddField( "Cód Filial"    , "Cód Filial"     , "M0_CODFIL" , "C", 50, 0, bValid, bWhen, , , bRelac, .F., , , )
		oStruct:AddField( "Nome Empresa"  , "Nome Empresa"   , "M0_NOMECOM", "C", 50, 0, bValid, bWhen, , , bRelac, .F., , , )
		oStruct:AddField( "CNPJ"          , "CNPJ"           , "M0_CGC"    , "C", 50, 0, bValid, bWhen, , , bRelac, .F., , , )
		oStruct:AddField( "UF"            , "UF"             , "M0_ESTENT" , "C", 50, 0, bValid, bWhen, , , bRelac, .F., , , )
		oStruct:AddField( "Insc Estadual" , "Insc Estadual"  , "M0_INSC"   , "C", 50, 0, bValid, bWhen, , , bRelac, .F., , , )
		oStruct:AddField( "Insc Municipal", "Insc Municipal" , "M0_INSCM"  , "C", 50, 0, bValid, bWhen, , , bRelac, .F., , , )
		oStruct:AddField( "Cód Munic"     , "Cód Munic"      , "M0_CODMUN" , "C", 50, 0, bValid, bWhen, , , bRelac, .F., , , )
		oStruct:AddField( "Nome Filial"   , "Nome Filial"    , "M0_FILIAL" , "C", 50, 0, bValid, bWhen, , , bRelac, .F., , , )  
		oStruct:AddField( "Município"     , "Município"      , "M0_CIDENT" , "C", 50, 0, bValid, bWhen, , , bRelac, .F., , , )  
		oStruct:AddField( "Inscrição"     , "Inscrição"      , "M0_INSCANT", "C", 50, 0, bValid, bWhen, , , bRelac, .F., , , )  
		oStruct:AddField( "NIRE"          , "NIRE"           , "M0_NIRE"   , "C", 50, 0, bValid, bWhen, , , bRelac, .F., , , )  
		oStruct:AddField( "Data do Nire"  , "Data do Nire"   , "M0_DTRE"   , "D", 08, 0, bValid, bWhen, , , bRelac, .F., , , )  
		oStruct:AddField( "End Cob"       , "End Cob"        , "M0_ENDCOB" , "C", 50, 0, bValid, bWhen, , , bRelac, .F., , , )  
         
Return oStruct

Static Function MyLoad()
Local aRet := {}
	aRet := {{SM0->M0_CODIGO, SM0->M0_CODFIL, SM0->M0_NOMECOM, SM0->M0_CGC, SM0->M0_ESTENT, SM0->M0_INSC, SM0->M0_INSCM, SM0->M0_CODMUN,;
	          SM0->M0_FILIAL, SM0->M0_CIDENT, SM0->M0_INSCANT, SM0->M0_NIRE, SM0->M0_DTRE, SM0->M0_ENDCOB}, ;
	         SM0->(Recno())}
Return aRet

 

 

 

 

 

 

#INCLUDE "TOTVS.CH"#INCLUDE "FWMVCDEF.CH"
// Função DummyFunction __FwRestModel__()Return 
//
SetStatusResponse(nStatus, cStatus) Class FwRestModel
	self:nStatus := nStatus
	self:cStatus := cStatus
Return 
//-------------------------------------------------------------------
/*/{Protheus.doc} SetQueryString
Método responsável por setar as query strings recebidas na requisicao.
@author Felipe Bonvicini Conti
@since 29/03/2016
@version P11, P12
/*/
//-------------------------------------------------------------------
Method SetQueryString(aQueryString) Class FwRestModel
Default aQueryString := {}
Return self:aQueryString := aQueryString
//-------------------------------------------------------------------
/*/{Protheus.doc} GetQueryString
Método responsável por retornar aa query strings recebidas na requisicao.
@author Felipe Bonvicini Conti
@since 29/03/2016
@version P11, P12
/*/
//-------------------------------------------------------------------
Method GetQueryString() Class FwRestModel
Return self:aQueryString
//-------------------------------------------------------------------
/*/{Protheus.doc} GetQSValue
Método responsável por buscar uma query string e retornar seu valor.
@author Felipe Bonvicini Conti
@since 29/03/2016
@version P11, P12
/*/
//-------------------------------------------------------------------
Method GetQSValue(cKey) Class FwRestModel
Local cValue := ""
Local nPos
	cKey := UPPER(cKey)
	nPos := aScan(self:aQueryString, {|x| x[1] == cKey})
	If nPos > 0
		cValue := self:aQueryString[nPos][2]
	EndIf
Return cValue
//-------------------------------------------------------------------
/*/{Protheus.doc} GetHttpHeader
Função de retorno do conteudo do cabeçalho HTTP.
@param cParam Parametro HTTP para busca
@return cReturn Valor do parametro HTTP solicitado. Se retornado NULL, o parametro não foi encontrado.
@author Felipe Bonvicini Conti
@since 30/03/2016
@version P11, P12
/*/
//-------------------------------------------------------------------
Method GetHttpHeader(cParam) Class FwRestModel
Return HTTPHeader(cParam)
//-------------------------------------------------------------------
/*/{Protheus.doc} SetFields
Método responsável por setar os campso que serao retornados no modelo
@param aFields Array com os campos a serem filtrados
@return lRet Retorna verdadeiro se os campos forem salvos corretamente
@author Felipe Bonvicini Conti
@since 29/03/2016
@version P11, P12
/*/
//-------------------------------------------------------------------
Method SetFields(aFields) Class FwRestModel
Default aFields := ""
	self:aFields := StrTokArr(aFields, ",")
Return Len(self:aFields) > 0
//-------------------------------------------------------------------
/*/{Protheus.doc} SetFields
Método responsável por setar os campso que serao retornados no modelo
@param lDebug Parametro para informara se o debug sera habilitado
@return lRet Indica se o begub foi abilitado
@author Felipe Bonvicini Conti
@since 29/03/2016
@version P11, P12
/*/
//-------------------------------------------------------------------
Method debuger(lDebug) Class FwRestModel
Default lDebug := .F.
Return self:lDebug := lDebug
// ********************* Functions *********************
//-------------------------------------------------------------------
/*/{Protheus.doc} ErrorMessage
Funcao responsavel por retonar o erro do modelo.
@param aErroMsg Array de erro do modelo de dados
@return cRet Formato texto do array de erro do modelo de dados
@author Felipe Bonvicini Conti
@since 05/04/2016
@version P11, P12
/*/
//-------------------------------------------------------------------
Static Function ErrorMessage(aErroMsg)
Local cRet := CRLF + " --- Error on Model ---" + CRLF
	cRet += "Id submodel origin: [" + aErroMsg[1] + "]" + CRLF
	cRet += "Id field origin: [" + aErroMsg[2] + "]" + CRLF
	cRet += "Id submodel error: [" + aErroMsg[3] + "]" + CRLF
	cRet += "Id field error: [" + aErroMsg[4] + "]" + CRLF
	cRet += "Id error: [" + aErroMsg[5] + "]" + CRLF
	cRet += "Error menssage: [" + aErroMsg[6] + "]" + CRLF
	cRet += "Solution menssage: [" + aErroMsg[7] + "]" + CRLF
	cRet += "Assigned value: [" + cValToChar( aErroMsg[8] ) + "]" + CRLF
	cRet += "Previous value: [" + cValToChar( aErroMsg[9] ) + "]" + CRLF
	aErroMsg := aSize(aErroMsg, 0)
Return cRet
//-------------------------------------------------------------------
/*/{Protheus.doc} GetFromQryAlias
Funcao responsavel por retornar a clausula from da query de dados
@param cTable Tablea principal
@return cFrom Clausula from da query
@author Felipe Bonvicini Conti
@since 05/04/2016
@version P11, P12
/*/
//-------------------------------------------------------------------
Static Function GetFromQryAlias(cTable)
Return " FROM " + RetSqlName( cTable )
//-------------------------------------------------------------------
/*/{Protheus.doc} GetWhereQryAlias
Funcao responsavel por retornar a clausula where da query de dados
@param cTable 	Tablea principal
@param cFilter	Filtro da query
@return cWhere Clausula where da query
@author Felipe Bonvicini Conti
@since 05/04/2016
@version P11, P12
/*/
//-------------------------------------------------------------------
Static Function GetWhereQryAlias(cTable, cFilter)
Local cWhere
Local cUsrFilFilter
	cWhere := " WHERE D_E_L_E_T_ = ' '"
	//Introduz a segurança padrão de acesso as filiais do sistema
	If (cTable)->(FieldPos(PrefixoCpo(cTable)+"_FILIAL")) > 0
		cUsrFilFilter := FWSQLUsrFilial(cTable)
		If !Empty(cUsrFilFilter)
			cWhere += " AND " + cUsrFilFilter
		EndIf
	EndIf
	If !Empty(cFilter)
		cWhere += " AND " + cFilter
	EndIf
Return cWhere
//-------------------------------------------------------------------
/*/{Protheus.doc} GetWhereQryAlias
Funcao responsavel por retornar a query do modelo
@param cQryAlias 	Alias da query
@param cTable 		Tablea principal
@param cFilter		Filtro da query
@return cQuery Query
@author Felipe Bonvicini Conti
@since 05/04/2016
@version P11, P12
/*/
//-------------------------------------------------------------------
-----------------/*/{Protheus.doc} FwRestModelClasse base para controlar o acesso aos registros relacionados ao modelo de dados.
@author Felipe Bonvicini Conti@since 25/06/2015@version P11, P12/*///-------------------------------------------------------------------Class FwRestModel Data cName Data cAlias Data oModel Data lXml Data lActivate Data cFilter Data cOldFilter Data nStatus Data cStatus
 Method Activate() Method DeActivate() Method OnError()
 Method SetModel() Method ClearModel() Method SetName() Method GetName() Method SetAsXml() Method SetAsJson()
 Method StartGetFormat() Method EscapeGetFormat() Method EndGetFormat()
 Method SetAlias() Method HasAlias() Method Seek() Method Skip() Method Total() Method GetData() Method SaveData() Method DelData()
 Method SetFilter() Method GetFilter() Method ClearFilter() Method DecodePK() Method ConvertPK()
 Method GetStatusResponse() Method SetStatusResponse()EndClass
//-------------------------------------------------------------------/*/{Protheus.doc} ActivateMétodo de ativação da classe.
@return lActivate Indica que a classe foi ativada.
@author Felipe Bonvicini Conti@since 25/06/2015@version P11, P12/*///-------------------------------------------------------------------Method Activate() Class FwRestModel self:lXml := .T.Return self:lActivate := .T.
//-------------------------------------------------------------------/*/{Protheus.doc} DeActivateMétodo de desativação da classe.
@author Felipe Bonvicini Conti@since 25/06/2015@version P11, P12/*///-------------------------------------------------------------------Method DeActivate() Class FwRestModel self:ClearModel() self:ClearFilter() self:lActivate := .F.Return 
//-------------------------------------------------------------------/*/{Protheus.doc} OnErrorMétodo que será executado quando algum erro ocorrer no rest.
@author Felipe Bonvicini Conti@since 25/06/2015@version P11, P12/*///-------------------------------------------------------------------Method OnError() Class FwRestModel self:DeActivate()Return 
//-------------------------------------------------------------------/*/{Protheus.doc} SetModelMétodo para setar o modelo de dados que será utilizado.E força a configuração do alias a ser utilizado.
@param oModel Objeto do modelo de dados
@return oModel Objeto do modelo de dados setado.
@author Felipe Bonvicini Conti@since 25/06/2015@version P11, P12/*///-------------------------------------------------------------------Method SetModel(oModel) Class FwRestModel self:oModel := oModel self:SetAlias()Return oModel
//-------------------------------------------------------------------/*/{Protheus.doc} ClearModelMétodo para efetuar o desroy do modelo de dados. 
@author Felipe Bonvicini Conti@since 25/06/2015@version P11, P12/*///-------------------------------------------------------------------Method ClearModel() Class FwRestModel If self:oModel != Nil self:oModel:Destroy() self:oModel := Nil EndIfReturn 
//-------------------------------------------------------------------/*/{Protheus.doc} SetNameMétodo para setar o nome do rest do modelo de dados. 
@return cName Nome do rest do modelo de dados.
@author Felipe Bonvicini Conti@since 25/06/2015@version P11, P12/*///-------------------------------------------------------------------Method SetName(cName) Class FwRestModelReturn self:cName := cName
//-------------------------------------------------------------------/*/{Protheus.doc} GetNameMétodo para retornar o nome do rest do modelo de dados. 
@return cName Nome do rest do modelo de dados.
@author Felipe Bonvicini Conti@since 25/06/2015@version P11, P12/*///-------------------------------------------------------------------Method GetName() Class FwRestModelReturn self:cName
//-------------------------------------------------------------------/*/{Protheus.doc} SetAsXmlMétodo para setar o retorno do rest como XML. 
@author Felipe Bonvicini Conti@since 25/06/2015@version P11, P12/*///-------------------------------------------------------------------Method SetAsXml() Class FwRestModel self:lXml := .T.Return 
//-------------------------------------------------------------------/*/{Protheus.doc} SetAsJsonMétodo para setar o retorno do rest como JSON. 
@author Felipe Bonvicini Conti@since 25/06/2015@version P11, P12/*///-------------------------------------------------------------------Method SetAsJson() Class FwRestModel self:lXml := .F.Return 
//-------------------------------------------------------------------/*/{Protheus.doc} StartGetFormatMétodo retornar o conteúdo inicial do dado de retorno.
@param nTotal Quantidade total de registros do alias que podem ser retornados.@param nCount Quantidade de registros a serem retornados.@param nStartIndex Index inicial do registro que será retornado.@return cRet Conteúdo inicial
@author Felipe Bonvicini Conti@since 25/06/2015@version P11, P12/*///-------------------------------------------------------------------Method StartGetFormat(nTotal, nCount, nStartIndex) Class FwRestModelLocal cRet := "" If self:lXml cRet += '<?xml version="1.0" encoding="UTF-8"?>' cRet += '<result>' cRet += i18n('<total>#1</total><count>#2</count><startindex>#3</startindex>', {nTotal, nCount, nStartIndex}) cRet += '<resources>' Else cRet += i18n('{"total":#1,"count":#2,"startindex":#3,"resources":[', {nTotal, nCount, nStartIndex}) EndIfReturn cRet
//-------------------------------------------------------------------/*/{Protheus.doc} EscapeGetFormatMétodo retornar o caracter a ser inserido entre os registros a serem retornados.
@return cRet Caracter de escape.
@author Felipe Bonvicini Conti@since 25/06/2015@version P11, P12/*///-------------------------------------------------------------------Method EscapeGetFormat() Class FwRestModelLocal cRet := "" If !self:lXml cRet := "," EndIfReturn cRet
//-------------------------------------------------------------------/*/{Protheus.doc} EndGetFormatMétodo retornar o conteúdo final do dado de retorno.
@return cRet Conteúdo final
@author Felipe Bonvicini Conti@since 25/06/2015@version P11, P12/*///-------------------------------------------------------------------Method EndGetFormat() Class FwRestModelLocal cRet := "" If self:lXml cRet := "</resources>" cRet += "</result>" Else cRet := "]}" EndIfReturn cRet
//-------------------------------------------------------------------/*/{Protheus.doc} SetAliasMétodo responsável por setar o alias a ser utilizado.Se o mesmo não for informado, será utilizado o alias do field principal do modelo de dados.
@return lRet Indica se o alias foi preenchido
@author Felipe Bonvicini Conti@since 25/06/2015@version P11, P12/*///-------------------------------------------------------------------Method SetAlias(cAlias) Class FwRestModel If Empty(cAlias) If !Empty(self:oModel) self:cAlias := MPGetModelAlias(self:oModel) dbSelectArea(self:cAlias) EndIf Else self:cAlias := cAlias EndIfReturn !Empty(self:cAlias)
//-------------------------------------------------------------------/*/{Protheus.doc} HasAliasMétodo responsável informar se o alias foi setado, se não força efetuaro setAlias().
@return lRet Indica se o alias foi preenchido
@author Felipe Bonvicini Conti@since 25/06/2015@version P11, P12/*///-------------------------------------------------------------------Method HasAlias() Class FwRestModelLocal lRet := .T. If Empty(self:cAlias) self:SetAlias() lRet := !Empty(self:cAlias) EndIfReturn lRet
//-------------------------------------------------------------------/*/{Protheus.doc} SeekMétodo responsável buscar um registro em específico no alias selecionado.Se o parametro cPK não for informaodo, indica que deve-se ser posicionadono primeiro registro da tabela.
@param cPK PK do registro.@return lRet Indica se foi encontrado algum registro.
@author Felipe Bonvicini Conti@since 25/06/2015@version P11, P12/*///-------------------------------------------------------------------Method Seek(cPK) Class FwRestModelLocal lRet := .F.Local nOrder If self:HasAlias() RefreshAlias(self:cAlias) If Empty(cPK) (self:cAlias)->(DbGotop()) lRet := !(self:cAlias)->(Eof()) Else nOrder := MPGetBestOrder(self:cAlias, self:oModel:GetPrimaryKey(), 1) (self:cAlias)->(dbSetOrder(nOrder)) lRet := (self:cAlias)->(DbSeek(cPK)) Endif EndifReturn lRet
//-------------------------------------------------------------------/*/{Protheus.doc} SkipMétodo responsável passar para o proximo registro do alias.
@return lRet Indica se o alias não chegou ao fim.
@author Felipe Bonvicini Conti@since 25/06/2015@version P11, P12/*///-------------------------------------------------------------------Method Skip() Class FwRestModelLocal lRet := .F. If self:HasAlias() (self:cAlias)->(DbSkip()) lRet := !(self:cAlias)->(Eof()) EndIfReturn lRet
//-------------------------------------------------------------------/*/{Protheus.doc} TotalMétodo responsável retornar a quantidade total de regitros do alias.Contagem é feita atravez de query.
@return nTotal Quantidade total de registros.
@author Felipe Bonvicini Conti@since 25/06/2015@version P11, P12/*///-------------------------------------------------------------------Method Total() Class FwRestModelLocal nTotal := 0 If self:HasAlias() nTotal := FWTblCount(self:cAlias,,.T.) EndIfReturn nTotal
//-------------------------------------------------------------------/*/{Protheus.doc} GetDataMétodo responsável por retornar o registro do modelo no formato XML ou JSON.
@param lFieldDetail Indica se retorna o registro com informações detalhadas@param lFieldVirtual Indica se retorna o registro com campos virtuais@param lFieldEmpty  Indica se retorna o registro com campos nao obrigatorios vazios@return cRet Retorna o registro nos formatos XML ou JSON
@author Felipe Bonvicini Conti@since 25/06/2015@version P11, P12/*///-------------------------------------------------------------------Method GetData(lFieldDetail, lFieldVirtual, lFieldEmpty) Class FwRestModelLocal cRet
 self:oModel:SetOperation(MODEL_OPERATION_VIEW) Self:oModel:Activate() If self:lXml cRet := Self:oModel:GetXmlData(lFieldDetail,,,lFieldVirtual,,lFieldEmpty,.F./*lDefinition*/,,.T./*lPK*/,.T./*lPKEncoded*/) Else cRet := Self:oModel:GetJsonData(lFieldDetail,,lFieldVirtual,,lFieldEmpty,.T./*lPK*/,.T./*lPKEncoded*/) EndIf Self:oModel:DeActivate()
Return cRet
//-------------------------------------------------------------------/*/{Protheus.doc} SaveDataMétodo responsável por salvar o registro recebido pelo metodo PUT ou POST.Se o parametro cPK não for informado, significa que é um POST.
@param cPK PK do registro.@param cData Conteúdo a ser salvo@param @cError Retorna o alguma mensagem de erro@return lRet Indica se o registro foi salvo
@author Felipe Bonvicini Conti@since 25/06/2015@version P11, P12/*///-------------------------------------------------------------------Method SaveData(cPK, cData, cError) Class FwRestModellocal lRet := .T.
Default cData := ""
 If Empty(cPk) self:oModel:SetOperation(MODEL_OPERATION_INSERT) Else self:oModel:SetOperation(MODEL_OPERATION_UPDATE) lRet := self:Seek(cPK) EndIf
 If lRet self:oModel:Activate() If self:lXml lRet := self:oModel:LoadXMLData(cData) Else lRet := self:oModel:LoadJsonData(cData) EndIf
 If lRet If self:oModel:lModify // Verifico se o modelo sofreu alguma alteração If !(self:oModel:VldData() .And. self:oModel:CommitData()) lRet := .F. cError := ErrorMessage(self:oModel:GetErrorMessage()) EndIf Else lRet := .F. self:SetStatusResponse(403, "Not Modified") EndIf Else cError := ErrorMessage(self:oModel:GetErrorMessage()) EndIf Self:oModel:DeActivate() Else cError := i18n("Invalid record '#1' on table #2", {cPK, self:cAlias}) EndIfReturn lRet
//-------------------------------------------------------------------/*/{Protheus.doc} DelDataMétodo responsável por remover um registro.
@param cPK PK do registro.@param @cError Retorna o alguma mensagem de erro@return lRet Indica se o registro foi removido
@author Felipe Bonvicini Conti@since 25/06/2015@version P11, P12/*///-------------------------------------------------------------------Method DelData(cPK, cError) Class FwRestModellocal lRet := .F.
 If !Empty(cPK)
 If self:Seek(cPK) self:oModel:SetOperation(MODEL_OPERATION_DELETE) self:oModel:Activate() lRet := self:oModel:VldData() .And. self:oModel:CommitData() If !lRet cError := ErrorMessage(self:oModel:GetErrorMessage()) EndIf Self:oModel:DeActivate() Else cError := i18n("Invalid record '#1' on table #2", {cPK, self:cAlias}) EndIf
 EndIf
Return lRet
//-------------------------------------------------------------------/*/{Protheus.doc} SetFilterMétodo responsável por setar algum filtro que tenha sido informado por Query String no REST.
@param cFilter Valor do filtro a ser aplicado no alias@return lRet Indica se o filtro foi aplicado corretamente
@author Felipe Bonvicini Conti@since 25/06/2015@version P11, P12/*///-------------------------------------------------------------------Method SetFilter(cFilter) Class FwRestModelLocal lRet := .F.
 If !Empty(cFilter) self:cFilter := Alltrim(cFilter) If self:HasAlias() self:cOldFilter := (self:cAlias)->(dbFilter()) If !Empty(self:cOldFilter) (self:cAlias)->(dbClearFilter()) EndIf (self:cAlias)->(dbSetFilter(&('{|| '+self:cFilter+'}'), self:cFilter)) lRet := .T. EndIf EndIf
Return lRet
//-------------------------------------------------------------------/*/{Protheus.doc} GetFilterMétodo para retornar o conteúdo do filtro.
@return cFilter Conteúdo do filtro
@author Felipe Bonvicini Conti@since 25/06/2015@version P11, P12/*///-------------------------------------------------------------------Method GetFilter() Class FwRestModelReturn self:cFilter
//-------------------------------------------------------------------/*/{Protheus.doc} GetFilterMétodo responsável pro limpar o filtro setado.
@author Felipe Bonvicini Conti@since 25/06/2015@version P11, P12/*///-------------------------------------------------------------------Method ClearFilter() Class FwRestModel (self:cAlias)->(dbClearFilter()) If !Empty(self:cOldFilter) (self:cAlias)->(dbSetFilter(&('{|| '+AllTrim(self:cOldFilter)+'}'), self:cOldFilter)) self:cOldFilter := Nil EndIfReturn 
//-------------------------------------------------------------------/*/{Protheus.doc} DecodePKMétodo para indicar se deve ser feito o decode do paramtro cPK recebido no REST
@author Felipe Bonvicini Conti@since 25/06/2015@version P11, P12/*///-------------------------------------------------------------------Method DecodePK() Class FwRestModelReturn .T.
//-------------------------------------------------------------------/*/{Protheus.doc} ConvertPKMétodo responsável por converter a PK.
@author Felipe Bonvicini Conti@since 25/06/2015@version P11, P12/*///-------------------------------------------------------------------Method ConvertPK(cPK) Class FwRestModel If self:DecodePK() cPK := Decode64(cPK) EndIfReturn cPK
//-------------------------------------------------------------------/*/{Protheus.doc} GetStatusResponseMétodo responsável por retornar o codigo e descrição do status de retorno, quando necessario.
@author Felipe Bonvicini Conti@since 07/07/2015@version P11, P12/*///-------------------------------------------------------------------Method GetStatusResponse() Class FwRestModelLocal Ret If !(self:nStatus == Nil) Ret := {self:nStatus, self:cStatus} EndIfReturn Ret
//-------------------------------------------------------------------/*/{Protheus.doc} SetStatusResponseMétodo responsável por setar o codigo e descrição do status de retorno, quando necessario.
@author Felipe Bonvicini Conti@since 07/07/2015@version P11, P12/*///-------------------------------------------------------------------Method SetStatusResponse(nStatus, cStatus) Class FwRestModel self:nStatus := nStatus self:cStatus := cStatusReturn 
// ********************* Functions *********************
Static Function ErrorMessage(aErroMsg)Local cRet := CRLF + " --- Error on Model ---" + CRLF cRet += "Id submodel origin: [" + aErroMsg[1] + "]" + CRLF cRet += "Id field origin: [" + aErroMsg[2] + "]" + CRLF cRet += "Id submodel error: [" + aErroMsg[3] + "]" + CRLF cRet += "Id field error: [" + aErroMsg[4] + "]" + CRLF cRet += "Id error: [" + aErroMsg[5] + "]" + CRLF cRet += "Error menssage: [" + aErroMsg[6] + "]" + CRLF cRet += "Solution menssage: [" + aErroMsg[7] + "]" + CRLF cRet += "Assigned value: [" + cValToChar( aErroMsg[8] ) + "]" + CRLF cRet += "Previous value: [" + cValToChar( aErroMsg[9] ) + "]" + CRLF aErroMsg := aSize(aErroMsg, 0)Return cRet
//-------------------------------------------------------------------/*/{Protheus.doc} RefreshAliasMétodo responsável por atualizar o Alias.
@param cAlias Alias que deve ser atualizado.
@author Felipe Bonvicini Conti@since 30/07/2015@version P11, P12/*///-------------------------------------------------------------------Static Function RefreshAlias(cAlias) If Select(cAlias) > 0 (cAlias)->(dbCloseArea()) EndIf dbSelectArea(cAlias)Return

Static Function createQueryAlias(cQryAlias, cTable, cFilter)
Local oStatement
Local cQuery  := ""
	cQuery += "SELECT R_E_C_N_O_"
	cQuery += GetFromQryAlias(cTable)
	cQuery += GetWhereQryAlias(cTable, cFilter)
	oStatement := FWPreparedStatement():New(cQuery)
	cQuery     := oStatement:getFixQuery()
	cQuery     := ChangeQuery(cQuery)
	MPSysOpenQuery(cQuery, @cQryAlias)
	oStatement:Destroy()
	FwFreeObj(oStatement)
Return cQuery


Utilização


Esta API segue o padrão de REST.


Por exemplo:

Para retornar a lista de registros referente ao modelo de dados deve-se efetuar um GET sem informar a <PK>. Os registros listados terão filtrados pelas filiais que o usuário tem acesso (isso se o campo filial existir.)

Para inserir um registro deve-se efetuar um POST sem informar a <PK> e enviar no body o conteúdo a ser inserido.

Ao informar o parâmetro <PK> será acessado um registro em específico e assim podendo ser utilizado os métodos GET, PUT, DELETE.


QueryStrings


COUNT = Quantidade de registro que devem ser retornados (padrão: 10)

STARTINDEX = Indica a partir que qual index deverá ser retornado (padrão: 1)

FILTER = Filtro que será aplicado no método SetFilter()

FIELDDETAIL = Habilita mostrar mais informações nos campos do modelo (padrão: 10)

FIELDVIRTUAL = Habilita o retorno de campos virtuais (padrão: false)

FIELDEMPTY = Habilita o retorno de campos sem valores (padrão: false)

FIRSTLEVEL = Habilita o retorno dos sub modelos (padrão: true)

FIELDS = Indica os campos a serem filtrados no retorno do modelo, incluindo os sub modelos, caso não informado todos os campos serão retornados

DEBUG = Valor booleano para habilitar o modo debug (padrão: false)

CACHE = Indica se sera feito cache do total de registros por alias, refere-se ao valor do total no retorno (padrão: true)

INTERNALID = Indica se deve retornar o ID(Recno) como informação complementar das linhas do GRID (padrão: false)


Exemplo de utilização


Ao publicar um modelo de dados, se o mesmo utilizar alias, basta publicá-lo.

Caso o modelo tenha sido desenvolvido utilizando array ou carga manual, deve-se criar uma classe e herda-la da FwRestModel e sobrescrever os métodos necessários para atender a sua necessidade.


Exemplo de modelo de dados com customização da classe REST:

Bloco de código
languageruby
titleModelo de dados customizando a classe REST
linenumberstrue
#include "totvs.ch"
#include "fwmvcdef.ch"

PUBLISH MODEL REST NAME Branch RESOURCE OBJECT oRestBranch

Class oRestBranch From FwRestModel
Data lSm0Closed

Method Activate()
Method DeActivate()
Method Total()
Method SetAlias()
Method Skip()
Method Seek()
EndClass

Method Activate() Class oRestBranch
local lRet as logical

If _Super:Activate()
//Por uma regra de negócio, essa API não pode ser acessada aos domingos
If Dow( Date() ) == 1
//Atribuir .F. a propriedade self:lActivate
self:lActivate := .F.

//Retornar .F. no método
lRet := .F.

//( Opcional ) setar a mensagem
SetRestFault(403, "Forbidden access in sundays.")

Else
self:lSm0Closed := .F.
If Select("SM0") == 0
self:lSm0Closed := .T.
OpenSm0(, .F.)
EndIf
EndIf

Else
lRet := .F.

EndIf

Return lRet

Method DeActivate() Class oRestBranch
Local lRet as logical

If ( lRet := _Super:DeActivate() )
If self:lSm0Closed
SM0->(dbCloseArea())
EndIf
EndIf

Return lRet

Method Total() Class oRestBranch
Local nRecno as numeric
Local nTotal as numeric

nRecno := SM0->(Recno())
nTotal := 0

If self:Seek()
While !SM0->(Eof())
nTotal++
self:Skip()
End
EndIf
SM0->(dbGoTo(nRecno))
Return nTotal

Method SetAlias() Class oRestBranch
self:cAlias := "SM0"
Return .T.

Method Skip(nSkip) Class oRestBranch
Local lRet as logical

lRet := .F.

SM0->(DbSkip(nSkip))
lRet := !SM0->(Eof())

Return lRet

Method Seek(cPk) Class oRestBranch
Local lRet as logical

lRet := .F.

If Empty(cPK)
SM0->(DbGotop())
lRet := !SM0->(Eof())
Else
cPK := SubStr(cPK, Len(xFilial("SM0")) + 1) // Removo o valor da filial que e inserido automaticamente pelo model no valor da PK.
SM0->(dbSetOrder(1))
lRet := SM0->(DbSeek(cPK))
Endif

Return lRet

// MODELO DE DADOS

Static Function Modeldef()
Local oStruSM0 as object
Local oModel as object

oStruSM0 := DefStrModel()
oModel := FWFormModel():New( 'MYFILIAL',
{|| }, {|| }

,
{|| }, {|| }

)

oModel:AddFields( 'SM0MASTER', , oStruSM0,
{|| }, {|| }

,
{|oM| MyLoad() }

)
oModel:SetDescription( "Empresas Protheus" )
oModel:GetModel( 'SM0MASTER' ):SetDescription( "Empresas Protheus" )
oModel:SetPrimaryKey(
{"M0_CODIGO", "M0_CODFIL"}

)
Return oModel

Static Function DefStrModel()
Local oStruct as object
Local bValid as codeblock
Local bWhen as codeblock
Local bRelac as codeblock

oStruct := FWFormModelStruct():New()
bValid :=
{ || .T.}

bWhen :=
{ || }
bRelac := { || }

// TABELA
oStruct:AddTable( "SM0", {}, "Filiais",
{|| }

)

// INDICES
oStruct:AddIndex(1, "1", "M0_CODIGO", "Cód Empresa", "", "", .T.)

// CAMPOS
oStruct:AddField( "Cód Empresa" , "Cód Empresa" , "M0_CODIGO" , "C", 50, 0, bValid, bWhen, , , bRelac, .F., , , )
oStruct:AddField( "Cód Filial" , "Cód Filial" , "M0_CODFIL" , "C", 50, 0, bValid, bWhen, , , bRelac, .F., , , )
oStruct:AddField( "Nome Empresa" , "Nome Empresa" , "M0_NOMECOM", "C", 50, 0, bValid, bWhen, , , bRelac, .F., , , )
oStruct:AddField( "CNPJ" , "CNPJ" , "M0_CGC" , "C", 50, 0, bValid, bWhen, , , bRelac, .F., , , )
oStruct:AddField( "UF" , "UF" , "M0_ESTENT" , "C", 50, 0, bValid, bWhen, , , bRelac, .F., , , )
oStruct:AddField( "Insc Estadual" , "Insc Estadual" , "M0_INSC" , "C", 50, 0, bValid, bWhen, , , bRelac, .F., , , )
oStruct:AddField( "Insc Municipal", "Insc Municipal" , "M0_INSCM" , "C", 50, 0, bValid, bWhen, , , bRelac, .F., , , )
oStruct:AddField( "Cód Munic" , "Cód Munic" , "M0_CODMUN" , "C", 50, 0, bValid, bWhen, , , bRelac, .F., , , )
oStruct:AddField( "Nome Filial" , "Nome Filial" , "M0_FILIAL" , "C", 50, 0, bValid, bWhen, , , bRelac, .F., , , )
oStruct:AddField( "Município" , "Município" , "M0_CIDENT" , "C", 50, 0, bValid, bWhen, , , bRelac, .F., , , )
oStruct:AddField( "Inscrição" , "Inscrição" , "M0_INSCANT", "C", 50, 0, bValid, bWhen, , , bRelac, .F., , , )
oStruct:AddField( "NIRE" , "NIRE" , "M0_NIRE" , "C", 50, 0, bValid, bWhen, , , bRelac, .F., , , )
oStruct:AddField( "Data do Nire" , "Data do Nire" , "M0_DTRE" , "D", 08, 0, bValid, bWhen, , , bRelac, .F., , , )
oStruct:AddField( "End Cob" , "End Cob" , "M0_ENDCOB" , "C", 50, 0, bValid, bWhen, , , bRelac, .F., , , )

Return oStruct

Static Function MyLoad()
Local aRet := {}
aRet := {{SM0->M0_CODIGO, SM0->M0_CODFIL, SM0->M0_NOMECOM, SM0->M0_CGC, SM0->M0_ESTENT, SM0->M0_INSC, SM0->M0_INSCM, SM0->M0_CODMUN,;
SM0->M0_FILIAL, SM0->M0_CIDENT, SM0->M0_INSCANT, SM0->M0_NIRE, SM0->M0_DTRE, SM0->M0_ENDCOB}, ;
SM0->(Recno())}
Return aRet
Status do documento

Concluído

Data25/06/2015
Versão1.0
Versão anterior1.0
Autores