-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
7c373c7
commit bef38ad
Showing
8 changed files
with
320 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,161 @@ | ||
# INTEROPWAY REST | ||
Repositório com conjunto de classes para auxiliar no desenvolvimento de integrações que consomem serviços REST.<br/> | ||
Repository with a set of classes to help in the development of integrations that consume REST services. | ||
[Português Brasil](/README.pt-br.md) | ||
--- | ||
# Interopway REST | ||
|
||
How many times do we find ourselves rebuilding, copy-pasting, adapting, _Business Operations_ that make calls to REST services, and only adapting one or another part of the final code. This is annoying a lot. To resolve this our inconvenience, I present to you **Interopway REST,** a set of classes (a micro framework) that allows us to just add _Business Operation to Production_ and use it. | ||
|
||
Let's understand how this works. | ||
|
||
## Transport Objects | ||
|
||
The objects responsible for transporting the data are grouped in the **cjs.interopway.rest.dto** package. Below we see a class diagram of its structure. | ||
|
||
 | ||
|
||
In this package we find 3 classes: | ||
|
||
* **cjs.interopway.rest.dto.Base**: Base class for exchanging information by grouping HTTP request and response properties. | ||
* **Body**: A stream containing the body of the request/response. | ||
* **Headers**: A key:value array with the request/response headers. | ||
* **JSONPayload**: A dynamic object, so if the content is a JSON, we can access it in a more user-friendly way. | ||
* **cjs.interopway.rest.dto.Request:** Transport object used for requests, extends class **cjs.interopway.rest.dto.Base** adding the property: | ||
* **URL**: Endpoint address to call. | ||
* **cjs.interopway.rest.dto.Response:** Transport object used for requests, extends class **cjs.interopway.rest.dto.Base** adding the properties: | ||
* **HTTPStatus**: Request processing HTTP status code. | ||
* **InternalStatus**: IRIS internal status code. | ||
* **InternalStatusText:** Decomposition of the InternalStatus property into a readable string. | ||
|
||
## Messages | ||
|
||
The Request/Response messages are defined in the package **cjs.interopway.rest.bo.** In the class diagram below we present only the most generic messages in the hierarchy that define the standard structure. | ||
|
||
 | ||
|
||
There are 12 message classes in this package. | ||
|
||
* **cjs.interopway.rest.bo.BaseRequest:** Abstract class for requests containing a reference to **cjs.interopway.rest.dto.Request.** | ||
* **cjs.interopway.rest.bo.BaseResponse:** Abstract class for responses containing a reference to **cjs.interopway.rest.dto.Response.** | ||
* **cjs.interopway.rest.bo.DeleteRequest:** Extends the class **cjs.interopway.rest.bo.BaseRequest** is used to do a **DELETE.** | ||
* **cjs.interopway.rest.bo.DeleteResponse:** Extends the class **cjs.interopway.rest.bo.BaseResponse** recebe a resposta de um **DELETE.** | ||
* **cjs.interopway.rest.bo.GetRequest:** Extends the class **cjs.interopway.rest.bo.BaseRequest** is used to do a **GET.** | ||
* **cjs.interopway.rest.bo.GetResponse:** Extends the class **cjs.interopway.rest.bo.BaseResponse** recebe a resposta de um **GET.** | ||
* **cjs.interopway.rest.bo.PatchRequest:** Extends the class **cjs.interopway.rest.bo.BaseRequest** is used to do a **PATCH.** | ||
* **cjs.interopway.rest.bo.PatchResponse:** Extends the class **cjs.interopway.rest.bo.BaseResponse** receives a response from a **PATCH.** | ||
* **cjs.interopway.rest.bo.PostRequest:** Extends the class **cjs.interopway.rest.bo.BaseRequest** is used to do a **POST.** | ||
* **cjs.interopway.rest.bo.PostResponse:** Extends the class **cjs.interopway.rest.bo.BaseResponse** receives a response from a **POST.** | ||
* **cjs.interopway.rest.bo.PutRequest:** Extends the class **cjs.interopway.rest.bo.BaseRequest** is used to do a **PUT.** | ||
* **cjs.interopway.rest.bo.PutResponse:** Extends the class **cjs.interopway.rest.bo.BaseResponse** receives a response from a **PUT.** | ||
|
||
## Business Operation | ||
|
||
 | ||
|
||
The class **cjs.interopway.rest.bo.Operation** extends the default Business Operation **EnsLib.REST.Operation** which has some utility methods and is already configured with then **EnsLib.HTTP.OutboundAdapter** adapter. | ||
|
||
This Business Operation has 5 Business Operation Methods, which are the methods that are in the MessageMap and are triggered when request messages are received: | ||
|
||
* **Delete** | ||
* **Get** | ||
* **Patch** | ||
* **Post** | ||
* **Put** | ||
|
||
These 5 methods only dispatch the call to the **CallEndPoint** method, which is responsible for invoking the REST service according to each HTTP verb associated with each message. | ||
|
||
The **GetHttpRequest** method instantiates an object of type **%Net.HttpResponse** and fills in the HTTP headers. | ||
|
||
The **GetResponseHeaders** method retrieves all headers returned in the request response. | ||
|
||
The **BeforeCallEndPoint** and **AfterCallEndPoint** methods are hook methods to be implemented in Business Operation subclasses, to perform actions before and after invoking the EndPoint, respectively. An example of use would be to call a login method before requests, and process the response object. | ||
|
||
## Installation | ||
|
||
Installation takes place via studio by importing the xml file with the project sources contained in the **/src/xmlexport/Interopway_REST.xml.** | ||
|
||
The IPM package is also available for installation: | ||
|
||
```objectscript | ||
zpm "install interopway_rest" | ||
``` | ||
|
||
## Example of Use | ||
|
||
The project has a Test Production **cjs.interopway.rest.samples.InteropProducion** and a class with unit tests **cjs.interopway.rest.tests.InteropProduction**. Only the Business Operation is used in the tests, as he is the one who does all the work. | ||
|
||
 | ||
|
||
To run the unit tests, copy the **/src/xmlexport/Interopway\_REST.xml** | ||
file to a directory, such as C:\\temp\\test. | ||
|
||
Open a terminal session and assign the path in the global **^UnitTestRoot** | ||
|
||
```objectscript | ||
Set ^UnitTestRoot = "C:\temp\tests" | ||
``` | ||
|
||
After defining the global run the unit test: | ||
|
||
```objectscript | ||
Do ##Class(%UnitTest.Manager).RunTest("", "/nodelete") | ||
``` | ||
|
||
This line of code will import, compile, run the class containing the unit tests. The /nodelete flag keeps the class source on the server. | ||
|
||
Access the URL presented at the end of the execution and see the test results. | ||
|
||
 | ||
|
||
If everything went well, it should look something like the image below. | ||
|
||
 | ||
|
||
Below is the code for the **TestPostUser** and **CallBusinessService** method: | ||
|
||
```objectscript | ||
Method TestPostUser() As %Status | ||
{ | ||
#Dim statusCode As %Status = ..IsProductionRunning() | ||
#Dim request As cjs.interopway.rest.bo.PostRequest = ##Class(cjs.interopway.rest.bo.PostRequest).%New() | ||
#Dim response As cjs.interopway.rest.bo.PostResponse = "" | ||
#Dim requestDTO As cjs.interopway.rest.dto.Request = ##Class(cjs.interopway.rest.dto.Request).%New() | ||
#Dim user As %DynamicObject = {} | ||
// | ||
Set user.email = "[email protected]" | ||
Set user."first_name" = "Cristiano José" | ||
Set user."last_name" = "Silva" | ||
Set requestDTO.JSONPayload = user | ||
Set requestDTO.URL = ..#APIURL _ ..#USERENDPOINT | ||
Set request.RequestDTO = requestDTO | ||
Set statusCode = ..CallBusinesService(..#TARGETOPERATION, request, .response) | ||
If ($System.Status.IsOK(statusCode)) | ||
{ | ||
Do ..LogMessage("Usuário criado com uscesso: " _ response.ResponseDTO.JSONPayload.id) | ||
} | ||
Return statusCode | ||
} | ||
Method CallBusinesService(targetBusinessOperation As %String, request As cjs.interopway.rest.bo.BaseRequest, ByRef response As cjs.interopway.rest.bo.BaseResponse) As %Status [ Private ] | ||
{ | ||
#Dim statusCode As %Status = $System.Status.OK() | ||
#Dim exception As %Exception.General = "" | ||
Try | ||
{ | ||
Set statusCode = ##Class(EnsLib.Testing.Service).SendTestRequest(targetBusinessOperation, request, .response,, 1) | ||
// | ||
Do $$$AssertStatusOK(statusCode, "Status Code from GetUser") | ||
// | ||
#Dim httpStatus As %Integer = $Extract(response.ResponseDTO.HTTPStatus, 1, 1) | ||
// Teste se o retorno é HTTPStatus está entre os status 200 é ok | ||
Do $$$AssertEquals(httpStatus, 2, "HTTP Status "_ response.ResponseDTO.HTTPStatus) | ||
} | ||
Catch (exception) | ||
{ | ||
Set statusCode = exception.AsStatus() | ||
} | ||
Return statusCode | ||
} | ||
``` | ||
|
||
The **TestPostUser** method instantiates a message to make the post of type **cjs.interopway.rest.bo.PostRequest** and fills the body with a dynamic object containing the JSON to be sent to the REST Service, assigns the url of the service and invokes the **CallBusinessService** method which invokes the testing service and dispatches the message to Business Operation. Note that all other test methods follow the same principle, except for the **GET** and **DELETE** method which have no body. | ||
|
||
With Interopway REST, we only add the Business Operation to Production, configure or pass the access data via messages and we worry about implementing the business rules of the integrations and no longer with the code to make the connection with external services. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
[English](/README.md) | ||
--- | ||
# Interopway REST | ||
|
||
Quantas vezes nos deparamos em reconstruir, copiar, colar adpatar, _Businesss Operations_ que fazem chamadas para serviços REST, e apenas adaptamos uma ou outra parte do código final. Isso incomada e muito. Para resolver esse nosso incoveniente apresento para vocês Interopway REST, um conjunto de classes (um micro framework) que nos permite apenas adicionar o _Business Operation à Production_ e utilizar. | ||
|
||
Vamos entender como isso funciona. | ||
|
||
## Objetos de Transporte | ||
|
||
Os objetos responsáveis por transportar os dados estão agrupados no pacote **cjs.interopway.rest.dto**. Abaixo vemos um diagrama de classes de sua estrutura. | ||
|
||
 | ||
|
||
Neste pacote encontramos 3 classes: | ||
|
||
* **cjs.interopway.rest.dto.Base**: Classe base para a troca de informações agrupando proriedades de requisição e resposta HTTP. | ||
* **Body**: Um stream contendo o corpo da requisição/resposta. | ||
* **Headers**: Um array de chave:valor com os cabeçalhos da requisição/resposta. | ||
* **JSONPayload**: Um objeto dinâmico, para caso o conteúdo seja um JSON, possamos acessá-lo de forma mais amigável. | ||
* **cjs.interopway.rest.dto.Request:** Objeto de transporte usado para as requisições, estende a classe **cjs.interopway.rest.dto.Base** adiconando a propriedade: | ||
* **URL**: Endreço do endpoint a ser chamado. | ||
* **cjs.interopway.rest.dto.Response:** Objeto de transporte usado para as resposta, estende a classe **cjs.interopway.rest.dto.Base** adiconando as propriedades: | ||
* **HTTPStatus**: Código de status HTTP do processamento da requisição. | ||
* **InternalStatus**: Código do status interno do IRIS. | ||
* **InternalStatusText:** Decomposição da propriedade InternalStatus em string de forma legível. | ||
|
||
## Mensagens | ||
|
||
As mensagens de _Request/Response_ estão definidas no pacote **cjs.interopway.rest.bo.** No diagrama de classes abaixo apresentamos apenas as mensagens mais genéricas na hierarquia que definem a estrutura padrão. | ||
|
||
 | ||
|
||
Existem 12 classes de mensagens neste pacote. | ||
|
||
* **cjs.interopway.rest.bo.BaseRequest:** Classe abstrata para as requisições contendo uma referência de **cjs.interopway.rest.dto.Request.** | ||
* **cjs.interopway.rest.bo.BaseResponse:** Classe abstrata para as respostas contendo uma referência de **cjs.interopway.rest.dto.Response.** | ||
* **cjs.interopway.rest.bo.DeleteRequest:** Estende a classe **cjs.interopway.rest.bo.BaseRequest** é utilizada para fazer um **DELETE.** | ||
* **cjs.interopway.rest.bo.DeleteResponse:** Estende a classe **cjs.interopway.rest.bo.BaseResponse** recebe a resposta de um **DELETE.** | ||
* **cjs.interopway.rest.bo.GetRequest:** Estende a classe **cjs.interopway.rest.bo.BaseRequest** é utilizada para fazer um **GET.** | ||
* **cjs.interopway.rest.bo.GetResponse:** Estende a classe **cjs.interopway.rest.bo.BaseResponse** recebe a resposta de um **GET.** | ||
* **cjs.interopway.rest.bo.PatchRequest:** Estende a classe **cjs.interopway.rest.bo.BaseRequest** é utilizada para fazer um **PATCH.** | ||
* **cjs.interopway.rest.bo.PatchResponse:** Estende a classe **cjs.interopway.rest.bo.BaseResponse** recebe a resposta de um **PATCH.** | ||
* **cjs.interopway.rest.bo.PostRequest:** Estende a classe **cjs.interopway.rest.bo.BaseRequest** é utilizada para fazer um **POST.** | ||
* **cjs.interopway.rest.bo.PostResponse:** Estende a classe **cjs.interopway.rest.bo.BaseResponse** recebe a resposta de um **POST.** | ||
* **cjs.interopway.rest.bo.PutRequest:** Estende a classe **cjs.interopway.rest.bo.BaseRequest** é utilizada para fazer um **PUT.** | ||
* **cjs.interopway.rest.bo.PutResponse:** Estende a classe **cjs.interopway.rest.bo.BaseResponse** recebe a resposta de um **PUT.** | ||
|
||
## Business Operation | ||
|
||
 | ||
|
||
A classe **cjs.interopway.rest.bo.Operation** estende o Business Operation padrão **EnsLib.REST.Operation** que possui alguns métodos utilitários e já vem configurado com o adaptador **EnsLib.HTTP.OutboundAdapter.** | ||
|
||
Este _Business Operation_ possui 5 _Business Operatino Methods,_ que são os métodos que estão no _MessageMap_ e são disparados no momento da recepção das mensagens de request, sendo eles: | ||
|
||
* **Delete** | ||
* **Get** | ||
* **Patch** | ||
* **Post** | ||
* **Put** | ||
|
||
Estes 5 métodos apenas despacham a chamada para o método **CallEndPoint** que é responsável por invocar o seviço REST de acordo com cada verbo HTTP associado a cada mensagem. | ||
|
||
O método **GetHttpRequest** instância um objeto do tipo **%Net.HttpResponse** e preenche os cabeçalhos HTTP. | ||
|
||
O método **GetResponseHeaders** recupera todos os cabeçalhos retornados na resposta da requisição. | ||
|
||
Os métodos **BeforeCallEndPoint** e **AfterCallEndPoint**, são métodos ganchos a serem implementados em subclasses do Business Operation para executarem ações respectivamente, antes e depois de invocar o _EndPoint_. Um exemplo de uso seria fazer uma chamada a um método de login antes das requisições, e processar o objeto de response. | ||
|
||
## Instalação | ||
|
||
A instalação se dá via studio importando o arquivo xml com os fontes do projeto contido no diretório **/src/xmlexport/Interopway_REST.xml.** | ||
|
||
O pacote IPM também está diponível para instalação: | ||
|
||
```objectscript | ||
zpm "install interopway_rest" | ||
``` | ||
|
||
## Exemplo de Uso | ||
|
||
No projeto existe uma _Production_ de testes **cjs.interopway.rest.samples.InteropProducion** e uma classe com testes unitários **cjs.interopway.rest.tests.InteropProduction. Apenas o** Business Operation é utilizado nos testes pois ele é quem faz todo o trabalho. | ||
|
||
 | ||
|
||
Para execução dos testes unitários copie o arquivo **/src/xmlexport/Interopway\_REST.xml** para algum diretório, como por exemplo, C:\\temp\\test. | ||
|
||
Abra uma sessão de terminal e atribua o caminho na global **^UnitTestRoot** | ||
|
||
```objectscript | ||
Set ^UnitTestRoot = "C:\temp\tests" | ||
``` | ||
|
||
Após definir a global execute o teste unitário: | ||
|
||
```objectscript | ||
Do ##Class(%UnitTest.Manager).RunTest("", "/nodelete") | ||
``` | ||
|
||
Essa linha de código ira importar, compilar, executar a classe contendo os testes unitários. A flag _/nodelete,_ mantém o fonte da classe no servidor. | ||
|
||
Acesse a URL apresentada ao final da execução e veja o resultado dos testes. | ||
|
||
 | ||
|
||
Se tudo correu bem, deverá ser algo pareceido com a imagem abaixo. | ||
|
||
 | ||
|
||
Abaixo o código do método **TestPostUser** e **CallBusinessService**: | ||
|
||
```objectscript | ||
Method TestPostUser() As %Status | ||
{ | ||
#Dim statusCode As %Status = ..IsProductionRunning() | ||
#Dim request As cjs.interopway.rest.bo.PostRequest = ##Class(cjs.interopway.rest.bo.PostRequest).%New() | ||
#Dim response As cjs.interopway.rest.bo.PostResponse = "" | ||
#Dim requestDTO As cjs.interopway.rest.dto.Request = ##Class(cjs.interopway.rest.dto.Request).%New() | ||
#Dim user As %DynamicObject = {} | ||
// | ||
Set user.email = "[email protected]" | ||
Set user."first_name" = "Cristiano José" | ||
Set user."last_name" = "Silva" | ||
Set requestDTO.JSONPayload = user | ||
Set requestDTO.URL = ..#APIURL _ ..#USERENDPOINT | ||
Set request.RequestDTO = requestDTO | ||
Set statusCode = ..CallBusinesService(..#TARGETOPERATION, request, .response) | ||
If ($System.Status.IsOK(statusCode)) | ||
{ | ||
Do ..LogMessage("Usuário criado com uscesso: " _ response.ResponseDTO.JSONPayload.id) | ||
} | ||
Return statusCode | ||
} | ||
Method CallBusinesService(targetBusinessOperation As %String, request As cjs.interopway.rest.bo.BaseRequest, ByRef response As cjs.interopway.rest.bo.BaseResponse) As %Status [ Private ] | ||
{ | ||
#Dim statusCode As %Status = $System.Status.OK() | ||
#Dim exception As %Exception.General = "" | ||
Try | ||
{ | ||
Set statusCode = ##Class(EnsLib.Testing.Service).SendTestRequest(targetBusinessOperation, request, .response,, 1) | ||
// | ||
Do $$$AssertStatusOK(statusCode, "Status Code from GetUser") | ||
// | ||
#Dim httpStatus As %Integer = $Extract(response.ResponseDTO.HTTPStatus, 1, 1) | ||
// Teste se o retorno é HTTPStatus está entre os status 200 é ok | ||
Do $$$AssertEquals(httpStatus, 2, "HTTP Status "_ response.ResponseDTO.HTTPStatus) | ||
} | ||
Catch (exception) | ||
{ | ||
Set statusCode = exception.AsStatus() | ||
} | ||
Return statusCode | ||
} | ||
``` | ||
|
||
O método **TestPostUser** intancia uma mensagem para fazer o post do tipo **cjs.interopway.rest.bo.PostRequest** e preenche o corpo com um objeto dinâmico conento o JSON a ser enviado para o Serviço REST, atribui a _URL_ do serviço e invoca o método **CallBusinessService** que invoca o serviço de testes e despacha a mensagem para o _Business Operation._ Note que todos os outros métodos de teste seguem o mesmo princípio, exceto para o método **GET** e **DELETE** que não possuem corpo. | ||
|
||
Com **Interopway REST** apenas adicionamos o _Business Operation à Production,_ configuaramos ou os passamos via mensagens os dados de acesso e nos preocupamos com a implementação das regras de negócio das integrações e não mais com o código para fazer a conexão com os serviços externos. |
Binary file added
BIN
+87.8 KB
images/cjs_interopway_rest_bo_BaseRequest-cjs_interopway_rest_bo_BaseResponse.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.