MILLENNIUM NETWORK
Millennium SDK 2.0
(beta)
Documentação Técnica (draft)
09/2013
Este documento contém as instruções para a utilização da API wtsDataSet que se presta à
comunicação de aplicativos de terceiros com o servidor de aplicações Millennium.
Conteúdo
1
Introdução ..................................................................................................................................... 3
2
Tipos de Chamadas ....................................................................................................................... 3
2.1
2.2
3
API Procedural ..............................................................................Erro! Indicador não definido.
2.1.1
Descrição ............................................................................................................... 3
2.1.2
Exemplos ............................................................................................................... 4
API de Entidades........................................................................................................................ 6
2.2.1
Introdução ............................................................................................................. 6
2.2.2
Sintaxe BNF simplificada da EQL ........................................................................... 9
2.2.3
Selecionando informações .................................................................................... 6
2.2.4
Agregações ............................................................................................................ 7
2.2.5
Restrições .............................................................................................................. 8
2.2.6
Parâmetros ............................................................................................................ 8
2.2.7
Exemplos ............................................................................................................... 8
API OData/REST ............................................................................................................................. 9
3.1
Configuração ............................................................................................................................. 9
3.2
Utilização ................................................................................................................................... 9
4
API WtsClientX............................................................................................................................. 11
4.1
Configuração ........................................................................................................................... 11
4.2
Interfaces................................................................................................................................. 11
4.3
4.2.1
IwtsDataSet ......................................................................................................... 11
4.2.2
IwtsCollection ...................................................................................................... 12
4.2.3
IwtsCollectionItem .............................................................................................. 12
Utilização ................................................................................................................................. 14
1 Introdução
O Servidor de aplicação Millennium (wtsBroker) é um serviço que permite a invocação de
procedimentos de negócios (chamados de transações) executando em um computador
servidor por um programa client.
Para permitir a chamada das transações expostas pelo servidor Millennium por qualquer
linguagem de programação do Windows, a Millennium desenvolveu um componente ActiveX
capaz de manipular e invocar as transações no servidor de aplicações por meio de TCP/IP
utilizando um protocolo extremamente compacto e eficiente. A versão 2.0 do SDK possui
também um gateway que permite a comunicação via OData/REST com o servidor. Este
protocolo utiliza apenas padrões abertos da internet, permitindo a qualquer linguagem capaz
de fazer chamadas HTTP acesso às funções do servidor.
Para maior adequação às várias necessidades de integração, foram expostas duas formas
comunicação com o servidor: i) Procedural, baseada em chamadas estruturadas ao servidor
(RPC) e ii) Declarativa, baseada em uma linguagem de consultas que age sobre o universo de
entidades do sistema.
Pedido
Cliente
Codificação
‘
Itens
SERVIDOR MILLENNIUM
TCP/IP, HTTP
Millenium.Pedido_Venda.Incluir
Decodificação
Select Cliente.Nome,
List(Cliente.Enderecos
.Logradouro)
Invocação
Validação
EQL
Serialização
Decodificação
Resultado
(ou) Exceção
TCP/IP, HTTP
Codificação
Figura 1: Esquema da comunicação Client/Server em 3 camadas do Millennium
Os capítulos a seguir detalharão as formas de comunicação OData/REST e ActiveX com
detalhes de configuração e exemplos em diversas linguagens.
2 Tipos de Chamadas
2.1 Métodos
2.1.1 Descrição
O paradigma utilizado para programar junto ao servidor é próximo ao de stored procedures,
já tradicionais nos bancos de dados relacionais. No Millennium, estas procedures se chamam
métodos e são divididas por objetos (produtos, clientes, vendas etc). A implementação destes
métodos reside em um servidor de aplicações, sendo que para o client, o que existe é apenas
um documento que possui parâmetros e resultados.
Uma característica dos métodos utilizados no Millennium é que eles suportam composição.
Por exemplo, uma transação de “inclusão de pedido de venda” possui um campo “produtos”,
que por sua vez é outra transação especial, chamada “registro”. Esta transação não pode ser
invocada, funcionando apenas como estrutura de dados. Esta capacidade permite o
preenchimento de todas as informações relevantes a um método de uma só vez,
possibilitando sua execução em uma só chamada ao servidor. Esta chamada ao servidor
Millennium é executada dentro de um mesmo contexto no banco de dados (transação),
melhorando o desempenho e garantindo a consistência dos dados.
Os parâmetros e resultados das transações podem ser inspecionados utilizando-se a
ferramenta ieditor.exe (Editor de Transações) fornecida no pacote. É importante ressaltar que
o catálogo de transações (*.wts) deve estar disponível tanto no client quanto no server
envolvidos no processo como consta no capítulo 2.1 – Configuração. Geralmente é necessário
requisitar esta biblioteca junto ao responsável pelo servidor, pois ela varia conforme a versão
instalada do Millennium.
2.1.2 Exemplos
Lista simples:
Var o = new wtsDataSet();
o.Transaction = “millenium.bancos.lista”;
o.Host = “localhost”;
o.ApplyDefaults();
o.Refresh();
while (!o.Eof())
{
Console.Print(o.Fields.ByName[“DESCRICAO”].Value);
o.Next()
}
Incluir um pedido:
o = CreateObject(“wtsclientx.wtsdataset”)
o.Transaction = 'millenium.pedido_venda.inclui'
‘quando o parâmetro é do tipo “R”, seu
‘valor é um outro dataset aninhado, ou seja
‘um dataset “filho”
ds = o.Params.ByName(“PRODUTOS”).Value()
ds.New()
ds.Fields.ByName(“PRODUTO”).Value = 9090
ds.Fields.ByName(“COR”).Value = 1
ds.Fields.ByName(“ESTAMPA”).Value = 1
ds.Fields.ByName(“TAMANHO”).Value = “P”
ds.Fields.ByName(“QUANTIDADE”).Value = 20
ds.Add; ‘adiciona registro de produto em dataset filho
‘armazena o dataset filho no dataset pai
o.Params.ByName(“PRODUTOS”).Value = ds
‘invoca o servidor de aplicação
o.Refresh
‘lê o Id do pedido de venda inserido
Debug.Print o.Fields.ByName(“PEDIDOV”).Value
2.2 Entidades
2.2.1 Introdução
O acesso às informações do Millennium através da API procedural, além de ser de fácil
compreensão e ter ótimo desempenho, organiza bastante o contrato entre cliente e servidor.
Apesar disto, existem situações onde é necessária maior flexibilidade, principalmente nas
consultas que na maioria dos casos não se beneficiam de uma interface padronizada,
procedural. Para estas situações, foi desenvolvida a EQL (Entity Query Language), uma
linguagem de consultas declarativa que expõe grande parte das entidades do Millennium de
forma similar à popular linguagem SQL. Esta linguagem permite maior flexibilidade, já que não
se baseia em um contrato pré-definido. Desta forma, é possível obter acesso às entidades do
sistema sem que um método novo tenha que ser criado para isto. Atualmente, a EQL é restrita
apenas à consulta de entidades. Para gravação, utilize a API procedural.
2.2.2 Selecionando informações
A estrutura da EQL se baseia nos segmentos Select e Where da SQL. No segmento Select
são listados os atributos das entidades que se deseja consultar na forma
Entidade[{.Entidade},...].Atributo [as Alias]. Por convenção, os nomes das entidades são no
singular. O aplicativo equery.exe permite a navegação pelas entidades e a criação de consultas
que podem ser utilizadas posteriormente como argumento EQL. Para consultar o nome de um
cliente, por exemplo, declara-se:
Select Cliente.Nome as Nome
Uma entidade relacionada também pode ser consultada de forma similar utilizando-se o
ponto (.) para indicar o relacionamento. Assim é possível acessar a descrição da categoria de
um produto da seguinte forma:
Select Produto.Categoria.Descricao as DescCateg
Uma diferença notória para o SQL é a ausência da cláusula FROM. Isto ocorre porque no
EQL todas as ligações entre entidades são conhecidas pelo sistema, tornando a ligação
explícita entre elas desnecessária. A possibilidade de acesso a relacionamentos de forma
explícita, como no exemplo acima, também torna possível obter controle sobre o contexto da
consulta sem a cláusula FROM. Se a ligação entre as entidades não for encontrada, um erro
será gerado. Existem casos onde a ligação entre entidades é inferida pelo sistema dependendo
dos elementos envolvidos. Veja alguns exemplos:
Esta consulta gera um erro, pois Cliente e Representante não são diretamente
relacionados.
Select Cliente.Nome, Representante.Nome
Esta consulta não gera erro, porque o contexto do pedido de venda é utilizada para ligar
cliente e fornecedor no pedido.
Select PedidoVenda.CodPedidoV, Cliente.Nome, Representante.Nome
Esta consulta também não gera erro, porque Cliente.Representante é o representante
padrão do cliente e está sendo indicado explicitamente.
Select Cliente.Nome, Cliente.Representante.Nome
Existem situações, onde se deseja consultar entidades e suas devidas coleções como, por
exemplo, um produto e suas cores disponíveis. Na EQL, uma coleção é retornada como um
“Record” do modelo procedural, ou seja o campo Cores do DataSet (IwtsDataSet) retornado
pode ser lido como um outro DataSet (IwtsSimpleDataSet) que possui uma lista de cores. Isto
pode ser feito através da expressão:
Select Produto.Descricao as DescProd, List(Produto.Cores.Descricao as DescCor) as Cores
2.2.3 Agregações
As agregações também funcionam de maneira semelhante ao SQL, suportando as
funções Sum, Min, Max, Avg, Count e CountD. A função CountD permite a contagem distinta
de itens, de forma similar ao Count(Distinct <Field>) do SQL. Apesar da sintaxe compatível, as
agregações em EQL possuem capacidades avançadas, baseadas na modelagem
multidimensional utilizada por DataWarehouses. Estas capacidades estão fora do escopo desta
documentação, mas é importante destacar a diferença de comportamento em relação à
resolução de agregados.
No SQL, quando uma agregação é feita, esta se aplica ao conjunto de tabelas
relacionado na cláusula “From”. Desta maneira, se torna difícil a confecção de consultas que
busquem informações relacionadas indiretamente como na pseudo-consulta abaixo:
Obtenha a quantidade pedida e o estoque atual dos produtos da categoria “Calça”
A única forma de responder esta consulta seria fazer uma consulta das quantidades
pedida das calças e outra das quantidades de estoque das mesmas. Também poderia ser
utilizada a capacidade de “subselect” do SQL, porém com perda significativa de desempenho,
já que “subselects” relacionados não podem ser executados de uma só vez:
Select p.Descricao, Sum(pe.Quantidade) as qtPedida, (select Sum(Saldo) from Estoques e
Where e.Produto=p.Produto) as qtEstoque From Produtos p Inner Join Pedidos PE on
p.Produto=PE.Produto Inner Join Categorias c on p.Categoria=c.Categoria
Where c.Nome = “Calça”
Na EQL, a mesma consulta pode ser expressa da seguinte forma:
Select Produto.Descricao, Sum(Pedido.Quantidade) as qtPedida,
Sum(Estoque.SaldoAtual) as qtEstoque Where Produto.Categoria.Nome = “Calça”
A diferença nos mecanismos, é que o EQL “entende” que Pedido e Estoque são “fatos” e
que Produto é uma “dimensão”, ou seja produto classifica registros de pedido e estoque.
Quando a consulta é executada, são geradas consultas independentes ao banco de dados que
depois são alinhadas, trazendo o resultado correto. Na verdade, as expressões agregadas EQL
possuem várias outras capacidades que são mais utilizadas em ferramentas de BI (Business
Intelligence) e estão fora do escopo da explanação da API de desenvolvimento.
2.2.4 Restrições
Para a aplicação de restrições, deve-se utilizar a cláusula Where após a cláusula Select, de
forma similar ao SQL. As expressões de comparação padrão =, <, >, >=, <= e <> podem ser
utilizadas, além do operadores IN LIST() / NOT IN LIST() para coleções. Um exemplo de uso
deste tipo de restrição segue abaixo, onde são listados os produtos que possuem cores
inativas:
Select Produto.Descricao where Produto.Produto in List(Produto.Cores.Ativo= ‘I’)
A EQL se diferencia do SQL no que trata a restrições sobre agregações, pois não é
necessária a separação entre WHERE e HAVING. Uma restrição agregada pode ser aplicada na
própria cláusula WHERE desde que não esteja combinada com uma restrição lógica OU com
um atributo:
Permitido:
Select Produto.Descricao where Sum(Venda.Total)>1000
Select Produto.Descricao where (Sum(Venda.Total)>1000) or
(Sum(Estoque.SaldoAtual)<20)
Não permitido:
Select Produto.Descricao where (Sum(Venda.Total)>1000) or (Produto.Codigo= “000”)
2.2.5 Parâmetros
É possível a passagem de parâmetros para a EQL sem o uso de literais na linha de
comando. Para isto, é utilizada a função Prompt:
Prompt(nome, tamanho,tipo de dados:[String,Integer,Float,Boolean])
Quando um parâmetro é utilizado, este fica acessível na coleção Params do DataSet,
podendo ser preenchido sem a posterior alteração do comando EQL.
2.2.6 Exemplos
Lista o Logradouro do primeiro Endereço de cada Filial:
‘Note que a classe é wtsmddataset
o = CreateObject(“wtsclientx.wtsmddataset”)
‘A Transaction é um comando EQL
o.Transaction = “select filial.nome as n,”_ &
“list(filial.enderecos.logradouro as l) as e”
‘Indicação do servidor
o.Host = “localhost”
‘Busca informações
o.Refresh
‘Lê cada registro retornado
While not o.Eof
‘Imprime o nome da filial
Debug.Print “Filial:” & o.Fields.ByName(“n”).Value
‘Note que o campo “e” possui outro dataset para cada registro
Set d = o.Fields.ByName(“e”).ValueAsDataSet
While not d.Eof
Debug.Print “Endereço:” & d.Fields.ByName(“l”).Value
d.Next
Wend
o.Next
Wend
2.2.7 Sintaxe BNF simplificada da EQL
SelectClause ::= “Select” {SelectMember} Where <Condition>;
EntityMember ::= Entity, {“.”,<Entity>}, “.”, Attribute;
SelectMember ::= EntityMember, [“as”, Alias];
Constant ::= String, Number;
Value ::= EntityMember | Constant;
ComparisonOp ::= “>”, “<”, “>=”, “<=”, “<>”, “in”;
Condition ::= EntityMember, ComparisonOp, Value;
3 API OData/REST
3.1 Configuração
3.2 Utilização
A forma pela qual as chamadas HTTP serão feitas para o uso desta API varia muito de
linguagem para linguagem, assim as instruções a seguir serão baseadas apenas no formato das
chamadas, sem se preocupar com a forma que elas serão feitas pelas linguagens e frameworks
de programação.
3.2.1 Operações de leitura
As operações de leitura podem ser feitas por meio de chamadas a métodos do Millennium ou
a operações sobre entidades. Cada entidade do Millennium possui operações padrão para
CRUD, ou seja, para as operações básicas de inclusão, alteração, consulta e exclusão. Estas
operações são tratadas de forma transparente pela API OData/REST para que sejam mapeadas
em verbos HTTP. Os verbos HTTP podem ser POST (inclusão), PUT (alteração total), MERGE
(alteração parcial), GET (leitura) ou DELETE (exclusão).
Para ler uma entidade, basta fazer uma chamada http GET:
GET https://<host>/api/millenium/<entidade>(<chave>)?$format=json|xml
A <entidade> é a entidade a ser lida e a <chave> neste caso será seu identificador. O
formato de retorno pode ser controlado pela opção $format que pode ser JSON ou XML.
O formato JSON é mais compacto e simples, retornando cada campo da entidade como
um par com atributo e valor. Os atributos podem ser valores, objetos ou arrays. A leitura da
entidade cliente identificada pelo número 10 em json, por exemplo, seria feita da seguinte
forma:
GET https://<host>/api/millenium/clientes(10)?$format=json
{“d”:[{
“cliente”:10,
“cod_cliente”: “000010”,
“data_cadastro”: “Date(452345343453)”,
“geradores”:
[{“nome”: “CLIENTE DE TESTE”,
“enderecos”:[{“endereco”:0, “cod_endereco”: “01”, “logradouro”: “RUA DE TESTE”},
{“endereco”:1, “cod_endereco”: “02”, “logradouro”: “RUA DE TESTE 2”}]
}]
}]}
Nas respostas JSON sempre há um array principal, chamado “d” com uma lista de
registros filhos, mesmo quando só há um registro, como neste caso. Cada item do array
principal é um objeto contendo os atributos do registro principal no formato especificado pela
convenção JSON. Esta especificação dita algumas normas que são seguidas pela API:
 Todos os atributos string usam códigos de escape apropriados para retorno de
caracteres reservados como quebras de linha, por exemplo.
 A codificação utilizada é a UTF8, garantindo também que todos os textos acentuados
funcionem corretamente. O formato de data
 Os campos de data são codificados utilizando-se o número de milissegundos a partir do
unix epoch (1/1/1970) envolvidos pela função Date().
Por ser um padrão, qualquer biblioteca JSON será capaz de decodificar os resultados e
transformá-los em objetos nativos de sua linguagem, portanto não é recomendável tentar
tratar os resultados manualmente. Junto à distribuição da API existem exemplos em diversas
linguagens de como fazer as chamadas e a decodificação do resultado.
4 API WtsClientX
4.1 Configuração
A API para acesso ao Servidor de Aplicações Millennium é implementada pela biblioteca
wtsclientx.dll. Por ser baseada na tecnologia COM (Component Object Model), esta deve ser
registrada com o programa RegSvr32.exe fornecida com o Windows™. É necessário também
que o catálogo de transações que será invocado no servidor esteja acessível. Para isto, deve-se
manter o arquivo *.wts no diretório da aplicação ou em qualquer diretório acessível pela
variável de ambiente PATH do Windows™.
4.2 Interfaces
4.2.1 IwtsDataSet
Classe: IwtsDataSet
Esta interface representa uma transação existente no Editor de Transações. Possibilita o
preenchimento dos parâmetros necessários, assim como a invocação e leitura dos resultados da
transação.
Nome
Descrição
function Eof: WordBool
Determina se o dataset está após seu
último registro
procedure Next
Avança um registro no cursor interno do
dataset
procedure Refresh
Método que invoca o servidor enviando
os parâmetros contidos na propriedade
Params e retornando o resultado que
pode ser lido por meio da propriedade
Fields.
procedure SaveToFile(const FileName: WideString)
Salva o conteúdo do dataset e seus
parâmetros em um arquivo
procedure LoadFromFile(const FileName: WideString)
Recupera o conteúdo do dataset e seus
parâmetros de um arquivo
procedure ApplyDefaults
Aplica os defaults especificados nos
parâmetros da transação nos
parâmetros do dataset
property Transaction: WideString
Lê e grava o nome da transação que será
invocada no servidor de aplicações
quando o método Refresh for chamado
property Fields: IwtsCollection
Lista de campos disponíveis da transação
property Params: IwtsCollection
Lista de parâmetros disponíveis da
transação
property RecordCount: Integer
Número de registros disponíveis para
leitura
4.2.2 IwtsCollection
Interface: IwtsCollection
Esta interface representa a lista de campos (Fields) e parâmetros (Params) da transação. Cada
item da lista pode ser acessado por índice ou nome.
Nome
Descrição
function Count: Integer
Determina o número de elementos da
lista
procedure SaveToFile(const FileName: WideString)
Salva os valores dos itens da lista em um
arquivo
procedure LoadFromFile(const FileName: WideString)
Recupera os valores dos itens da lista de
um arquivo
property ByName[const Name: WideString]:
IwtsCollectionItem
Acessa os itens da lista pelo nome
property Items[Index: Integer]: IwtsCollectionItem
Acessa os itens da lista por seu índice
property Ordered: WordBool
Determina se o conteúdo da lista será
ordenado segundo a declaração na
transação ou na ordem utilizada para a
interface com o usuário
4.2.3 IwtsCollectionItem
Interface: IwtsCollectionItem
Representa um item dos campos ou parâmetros da transação. Sua propriedade principal é a
“value”, que possui o valor atual do item. No caso de um “Field”, este valor muda, conforme o
dataset é movimentado.
Nome
Descrição
property Value: OleVariant
Valor atual deste item (pode ser
Params[] ou Fields[])
property ValueAsJPEG: OleVariant
Lê o conteúdo do campo como um array
de bytes codificado como uma imagem
JPEG
property ValueAsDataSet: IwtsSimpleDataSet
Lê o conteúdo do campo como um
DataSet aninhado
Metadatados
property Name: WideString
Nome do item
property Size: Integer
Tamanho máximo do valor do item
(aplicável principalmente a campos
“string”)
property Format: WideString
Formato do valor do item. Pode ter os
valores:
A: Texto
B: Booleano
N: Numérico
M: Monetário
D: Data
H: Data e Hora
T: Tempo
I: Imagem
R: Recordset
property Visible: WordBool
Determina se o item deve ser visível
para o usuário
property NotNull: WordBool
Determina se o item é obrigatório
property FieldLabel: WideString
Determina o texto que será utilizado
para rotular o item para o usuário
property Lookup: WideString
Nome da transação utilizada para busca
de lista de valores utilizados para
preenchimento do campo
property LookupKeyField: WideString
Campo chave da lista utilizado para
preenchimento do valor do item
property LookupDisplayCode: WideString
Campo que deverá ser utilizado como
chave na apresentação da lista ao
usuário
property LookupDisplay: WideString
Campo que deverá ser utilizado como
decrição na apresentação da lista ao
usuário
property Style: TInputStyle
Estilo que deve ser utilizado para
apresentação do item ao usuário:
stDefault: O estilo é determinado pelo
tipo do campo;
stPopupRowset: Sub-dataset que é
apresentado como uma tela popup;
stInplaceRowset: Sub-dataset que é
apresentado inline com os outros itens;
stGridRowset: Sub-dataset que é
apresentado inline com os outros itens
em forma de tabela;
stCheckList: Lista de itens apresentados
como checkbox de múltipla seleção
stFilteredLookup: Lista de valores
filtrados
property IsCustomLookup: WordBool
Determina se o item possui um lookup
customizado
property Ambient: WordBool
Determina se o valor do item será
preenchido automaticamente baseado
nos valores dos campos da transação
4.3 Utilização
O paradigma de acesso a dados utilizado pela API WtsClientX segue o padrão de
programação DataSet, onde as informações são expostas como Tabelas, Campos e Registros.
Existem duas classes principais que possuem interfaces idênticas: wtsDataSet e
wtsMDDataSet. Ambas possuem exatamente as mesmas propriedades e métodos, contudo a
propriedade Transaction de data uma recebe comandos distintos. No caso no wtsDataSet, esta
propriedade recebe o nome de um método, ou seja um procedimento pré-definido que será
invocado no servidor de aplicações. Já na classe wtsMDDataSet, esta propriedade recebe um
comando EQL (Entity Query Language) que também será executado no servidor de aplicações.
As duas formas de comandos serão explicadas posteriormente neste documento.
Depois de informado o comando por meio da propriedade Transaction, deve indicar o
servidor que será invocado. Isto se faz por meio da propriedade Host. O próximo passo é
preencher algum parâmetro, se for o caso, na propriedade Params[]. Se for o caso, pode-se
preencher estes parâmetros com os “defaults” do sistema por meio do método
ApplyDefaults(). Para invocar o servidor, utiliza-se o método Refresh() e para ler seu resultado
a propriedade Fields[]. Existem também as propriedades EOF para testar se o registro corrente
é o posterior ao final e o método Next() para movimentar-se adiante.
O acesso aos campos pode ser feito por posição [Fields[0].Value (Delphi, C#),
Fields(0).Value (VB)] ou por nome [Fields.ByName[‘Nome’].Value (Delphi, C#),
Fields.ByName(“Nome”).Value (VB)] que é o mais recomendável. É possível também tratar
DataSets aninhados em campos por meio do método ValueAsDataSet da propriedade Fields[].
No capítulo 5, constam as referências completas das classes da biblioteca.
Download

INTEGRAÇÃO_Documentação_2.0