Este artigo visa orientar sobre o desenho e implementação de APIs TOTVS respeitando as orientações do guia de implementação de APIs, bem como o modelo de conteúdo definido nas transações de mensagem padronizada TOTVS.
Informações |
---|
As orientações presentes neste documento estão em constante ajuste. Por isso, consulte sempre esta página quando for modelar uma nova transação. |
Partindo de um modelo XML como base, veremos a seguir um exemplo de uma possível implementação de API usando segmentação do modelo de dados.
O modelo XML utilizado será Contract_2_000.xsd que está representado graficamente a seguir (clique na imagem para expandir).
Convertendo este modelo "como ele é" para o formato OpenAPI 3.0, teríamos o seguinte documento (alguns elementos previstos nos guias de implementação de APIs e mensagem padronizada - REST/JSON foram omitidos para melhor compreensão):
Bloco de código | ||||||
---|---|---|---|---|---|---|
| ||||||
{ "openapi": "3.0.0", (...), "components": { "schemas": { "Contract": { "type": "object", "properties": { "CompanyId": { "type": "string" }, "BranchId": { "type": "string" }, "CompanyInternalId": { "type": "string" }, "InternalId": { "type": "string" }, "ContractNumber": { "type": "string" }, "ContractReview": { "type": "string" }, "ProjectInternalId": { "type": "string" }, "BeginDate": { "type": "string", "format": "date-time" }, "FinalDate": { "type": "string", "format": "date-time" }, "CustomerCode": { "type": "string" }, "CustomerInternalId": { "type": "string" }, "ContractTotalValue": { "type": "number", "format": "float" }, "ContractTypeCode": { "type": "string" }, "ContractTypeInternalId": { "type": "string" }, "ListOfSheet": { "type": "array", "items": { "type": "object", "properties": { "SheetNumber": { "type": "string" }, "SheetTypePoperty": { "type": "string" }, "UnitPrice": { "type": "number", "format": "float" }, "SheetTotalValue": { "type": "number", "format": "float" }, "ListOfItem": { "type": "array", "items": { "type": "object", "properties": { "ItemCode": { "type": "string" }, "ItemInternalId": { "type": "string" }, "AccountantAcountCode": { "type": "string" }, "AccountantAcountInternalId": { "type": "string" }, "CostCenterCode": { "type": "string" }, "CostCenterInternalId": { "type": "string" }, "AccountingItemCode": { "type": "string" }, "AccountingItemInternalId": { "type": "string" }, "ClassValueCode": { "type": "string" }, "ClassValueInternalId": { "type": "string" }, "ItemQuantity": { "type": "number", "format": "float" }, "ItemUnitPrice": { "type": "number", "format": "float" }, "ItemTotalValue": { "type": "number", "format": "float" }, "PercentageOfDiscount": { "type": "number", "format": "float" } } } } } } } }, "description": "Contrato" } } } } |
Entretanto, utilizar o modelo desta forma tem vários problemas como, por exemplo, na modificação do contrato, onde teríamos que enviar também as páginas (Sheet) do contrato e os itens das páginas.
Por isso, a segmentação do modelo de dados é permitida, desde que mantenha a estrutura e atributos do modelo XML original.
Nosso modelo OpenAPI poderia ser segmentado em 3 submodelos:
Convertendo isso para o modelo OpenAPI, teríamos o seguinte, lembrando que elementos como as tags de documentação foram omitidos por questões didáticas.
Bloco de código | ||||||
---|---|---|---|---|---|---|
| ||||||
{ "openapi": "3.0.0", (...), "components": { "schemas": { "ContractModel": { "type": "object", "properties": { "CompanyId": { "type": "string" }, "BranchId": { "type": "string" }, "CompanyInternalId": { "type": "string" }, "InternalId": { "type": "string" }, "ContractNumber": { "type": "string" }, "ContractReview": { "type": "string" }, "ProjectInternalId": { "type": "string" }, "BeginDate": { "type": "string", "format": "date-time" }, "FinalDate": { "type": "string", "format": "date-time" }, "CustomerCode": { "type": "string" }, "CustomerInternalId": { "type": "string" }, "ContractTotalValue": { "type": "number", "format": "float" }, "ContractTypeCode": { "type": "string" }, "ContractTypeInternalId": { "type": "string" }, "ListOfSheet": { "type": "array", "items": { "$ref": "#/components/schemas/SheetModel" } } }, "description": "Contrato" }, "SheetModel": { "type": "object", "properties": { "SheetNumber": { "type": "string" }, "SheetTypePoperty": { "type": "string" }, "UnitPrice": { "type": "number", "format": "float" }, "SheetTotalValue": { "type": "number", "format": "float" }, "ListOfItem": { "type": "array", "items": { "$ref": "#/components/schemas/ItemModel" } } } }, "ItemModel": { "type": "object", "properties": { "ItemCode": { "type": "string" }, "ItemInternalId": { "type": "string" }, "AccountantAcountCode": { "type": "string" }, "AccountantAcountInternalId": { "type": "string" }, "CostCenterCode": { "type": "string" }, "CostCenterInternalId": { "type": "string" }, "AccountingItemCode": { "type": "string" }, "AccountingItemInternalId": { "type": "string" }, "ClassValueCode": { "type": "string" }, "ClassValueInternalId": { "type": "string" }, "ItemQuantity": { "type": "number", "format": "float" }, "ItemUnitPrice": { "type": "number", "format": "float" }, "ItemTotalValue": { "type": "number", "format": "float" }, "PercentageOfDiscount": { "type": "number", "format": "float" } } } } } } |
Consequentemente, esta segmentação será refletida nos endpoints das APIs. Tomando por base a divisão realizada, teríamos o seguinte template:
Bloco de código | ||
---|---|---|
| ||
/api/{produto}/{agrupador}/v2.000/contracts/{ContractUniqueId}/sheets/{SheetNumber}/items/{ItemCode} |
Neste template, temos as seguintes considerações:
Nos exemplos a seguir veremos a utilização dos possíveis endpoints e seus respectivos modelos:
Inclusão de um contrato de forma completa
Bloco de código | ||||||
---|---|---|---|---|---|---|
| ||||||
{ "CompanyId": "1", "BranchId": "1", "CompanyInternalId": "1", "InternalId": "1|1|1", "ContractNumber": "1", "ContractReview": "1", "ProjectInternalId": "1|1", "BeginDate": "2018-07-25T14:24:00", "FinalDate": "2019-07-25T14:24:00", "CustomerCode": "1", "CustomerInternalId": "1", "ContractTotalValue": 1.0, "ContractTypeCode": "1", "ContractTypeInternalId": "1", "ListOfSheet": [{ "SheetNumber": "1", "SheetTypePoperty": "1", "UnitPrice": 1.0, "SheetTotalValue": 1.0, "ListOfItem": [{ "ItemCode": "1", "ItemInternalId": "1|1", "AccountantAcountCode": "0001", "AccountantAcountInternalId": "1|0001", "CostCenterCode": "1", "CostCenterInternalId": "1|1", "AccountingItemCode": "111", "AccountingItemInternalId": "1|111", "ClassValueCode": "001", "ClassValueInternalId": "1|001", "ItemQuantity": 1.0, "ItemUnitPrice": 1.0, "ItemTotalValue": 1.0, "PercentageOfDiscount": 0.0 } ] } ] } |
Inclusão de um contrato sem folhas (se a regra de negócio permitir)
Bloco de código | ||||||
---|---|---|---|---|---|---|
| ||||||
{ "CompanyId": "1", "BranchId": "1", "CompanyInternalId": "1", "InternalId": "1|1|1", "ContractNumber": "1", "ContractReview": "1", "ProjectInternalId": "1|1", "BeginDate": "2018-07-25T14:24:00", "FinalDate": "2019-07-25T14:24:00", "CustomerCode": "1", "CustomerInternalId": "1", "ContractTotalValue": 1.0, "ContractTypeCode": "1", "ContractTypeInternalId": "1", "ListOfSheet": [] } |
Recuperando contratos (apenas cabeçalho, 1a página, até o limite de uma página)
Bloco de código | ||||||
---|---|---|---|---|---|---|
| ||||||
{ "hasNext": false, "items": [{ "_expandables": ["ListOfSheet"], "CompanyId": "1", "BranchId": "1", "CompanyInternalId": "1", "InternalId": "1|1|1", "ContractNumber": "1", "ContractReview": "1", "ProjectInternalId": "1|1", "BeginDate": "2018-07-25T14:24:00", "FinalDate": "2019-07-25T14:24:00", "CustomerCode": "1", "CustomerInternalId": "1", "ContractTotalValue": 1.0, "ContractTypeCode": "1", "ContractTypeInternalId": "1", "ListOfSheet": [] } ] } |
Recuperando um contrato, expandindo os atributos ListOfSheet e ListOfItem
Bloco de código | ||||||
---|---|---|---|---|---|---|
| ||||||
{ "_expandables": ["ListOfSheet"], "CompanyId": "1", "BranchId": "1", "CompanyInternalId": "1", "InternalId": "1|1|1", "ContractNumber": "1", "ContractReview": "1", "ProjectInternalId": "1|1", "BeginDate": "2018-07-25T14:24:00", "FinalDate": "2019-07-25T14:24:00", "CustomerCode": "1", "CustomerInternalId": "1", "ContractTotalValue": 1.0, "ContractTypeCode": "1", "ContractTypeInternalId": "1", "ListOfSheet": [{ "_expandable": ["ListOfItem"], "SheetNumber": "1", "SheetTypePoperty": "1", "UnitPrice": 1.0, "SheetTotalValue": 1.0, "ListOfItem": [{ "ItemCode": "1", "ItemInternalId": "1|1", "AccountantAcountCode": "0001", "AccountantAcountInternalId": "1|0001", "CostCenterCode": "1", "CostCenterInternalId": "1|1", "AccountingItemCode": "111", "AccountingItemInternalId": "1|111", "ClassValueCode": "001", "ClassValueInternalId": "1|001", "ItemQuantity": 1.0, "ItemUnitPrice": 1.0, "ItemTotalValue": 1.0, "PercentageOfDiscount": 0.0 } ] } ] } |
Incluindo uma folha no contrato
Bloco de código | ||||||
---|---|---|---|---|---|---|
| ||||||
{ "SheetNumber": "1", "SheetTypePoperty": "1", "UnitPrice": 1.0, "SheetTotalValue": 1.0, "ListOfItem": [{ "ItemCode": "1", "ItemInternalId": "1|1", "AccountantAcountCode": "0001", "AccountantAcountInternalId": "1|0001", "CostCenterCode": "1", "CostCenterInternalId": "1|1", "AccountingItemCode": "111", "AccountingItemInternalId": "1|111", "ClassValueCode": "001", "ClassValueInternalId": "1|001", "ItemQuantity": 1.0, "ItemUnitPrice": 1.0, "ItemTotalValue": 1.0, "PercentageOfDiscount": 0.0 } ] } |
Incluindo um item de uma folha do contrato
Bloco de código | ||||||
---|---|---|---|---|---|---|
| ||||||
{ "ItemCode": "1", "ItemInternalId": "1|1", "AccountantAcountCode": "0001", "AccountantAcountInternalId": "1|0001", "CostCenterCode": "1", "CostCenterInternalId": "1|1", "AccountingItemCode": "111", "AccountingItemInternalId": "1|111", "ClassValueCode": "001", "ClassValueInternalId": "1|001", "ItemQuantity": 1.0, "ItemUnitPrice": 1.0, "ItemTotalValue": 1.0, "PercentageOfDiscount": 0.0 } |
Eliminando um item de uma folha
Eliminando uma folha de um contrato
O versionamento de APIs que atuam sobre entidades com mensagem padronizada seguirá o padrão definido para mensagem padronizada. Teremos evolução da versão da API sempre que houver mudança no modelo de dados, ainda que no Guia de APIs conste outra orientação.
O padrão será a letra "v" + versão da transação. Por exemplo: v2.000.
Exemplos de APIs versionadas:
No caso de APIs relacionadas a transações do tipo Request, considerar o exposto no tópico a seguir.
Quando a modelagem da API envolver uma operação a ser realizada numa entidade, deve-se ter em mente que existem mensagens padronizadas do tipo Request, que representam operações a realizar no sistema de destino.
Neste caso, há duas possibilidades de modelagem da API:
Modelar a operação vinculada a uma entidade de mensagem padronizada existente:
Consideremos a transação CancelRequest, versão 1.000. Esta transação representa o cancelamento de uma requisição, que é referenciada dentro da mensagem, junto com outras informações.
Uma possível API para esta transação seria:
Bloco de código | ||||||
---|---|---|---|---|---|---|
| ||||||
{ "Code": "10", "RequestInternalId": "100", "Type": "000", "CancelDateTime": "2018-07-20T12:00:34-03:00", "CancelReason": "Rejeitado", "CancelRelatedRequests": "" } |
Observe que a versão da API, neste caso, está relacionada a transação CancelRequest e não a transação Request, já que o corpo da requisição não faz uso do modelo de mensagem de Request. Este exemplo também está mais alinhado com a orientação do guia de APIs, para vincular ações como recurso de uma entidade.
Modelar a operação como um endpoint distinto da entidade que será atualizada:
Considerando a mesma transação CancelRequest, versão 1.000, teríamos o seguinte exemplo de API:
Bloco de código | ||||||
---|---|---|---|---|---|---|
| ||||||
{ "Code": "10", "RequestInternalId": "100", "Type": "000", "CancelDateTime": "2018-07-20T12:00:34-03:00", "CancelReason": "Rejeitado", "CancelRelatedRequests": "" } |
Nesta forma, o recurso é a própria operação, sendo que a entidade que será alterada está referenciada no corpo da requisição. Este modelo deixa mais claro o vínculo entre versão da API e modelo de mensagem padronizada.
Portal TOTVS para APIs e Mensagem Padronizada
Guia de implementação de APIs TOTVS
Elaboração de mensagem padronizada REST/JSON
Elaboração de mensagem padronizada SOAP/XML
Repositório de mensagens padronizadas (TFS) - STABLE - requer login TOTVS para acesso.
Repositório de mensagens padronizadas (TFS) - DEV - requer login TOTVS para acesso.
Mapa de clientes internos TOTVS - lista os Product Owners e demais papeis que devem ser envolvidos no processo de aprovação.
Editor Swagger/OpenAPI - permite a criação de documentos Swagger 2.0 ou OpenAPI 3.0 através da edição de tags YAML.
Conversor Swagger 2.0 para OpenAPI - converte um documento Swagger 2.0 em um documento OpenAPI 3.0.
Modelador de APIs Restlet Studio - permite a modelagem de APIs (endpoints, tipos de dados, etc.) gerando documentação em Swagger 2.0 e RAML 1.0.
http://docs.oasis-open.org/ubl/prd1-UBL-2.1/UBL-2.1.html
http://docs.oasis-open.org/ubl/UBL-2.1-JSON/v1.0/cnd02/UBL-2.1-JSON-v1.0-cnd02.html
http://docs.oasis-open.org/ubl/UBL-2.1-JSON/v1.0/cnd02/json-schema/maindoc/
http://www.unece.org/cefact/brs/brs_index.html
Painel | ||
---|---|---|
| ||
|