Desenvolva suas Funções

Para processar os dados, você precisará de três unidades de código independentes: uma função de transformação, uma função de carga e uma função de callback.

Essas funções foram implementadas usando o Oracle Functions e escritas em Python. O Oracle Functions é perfeitamente adequado a este job porque o carregamento de dados pode ser algo que acontece com frequência limitada (talvez uma ou duas vezes por hora ou por dia). O uso do Oracle Functions é vantajoso porque a função só é chamada quando há algo a ser feito e, quando terminar o processamento, ela é encerrada. Além disso, não há sistema operacional, roteamento ou outro servidor para manutenção; essa é uma arquitetura sem servidor. Para essa implementação de exemplo, escolhemos o Python em vez de outras opções de linguagem porque ela é facilmente compreendida e exendível, e não há um requisito rigoroso de desempenho para esse job de carregamento de dados.

Nossas três funções são:

  • Uma função de transformação para traduzir o arquivo de dados de um formato JSON simplificado para o arquivo Zip Específico do Oracle Cloud ERP
  • Uma função de carregamento para carregar o arquivo no Oracle Cloud ERP
  • Uma função de callback para tratar a resposta do Oracle Fusion

Cada uma dessas funções selecionará seus dados do bucket do OCI Storage, processará-os e depois os colocará em outro bucket.

Usar Buckets de Armazenamento do OCI

Primeiro, você precisa obter os dados no OCI Cloud para que possa processá-los com eficiência. O Oracle OCI tem uma opção ideal para esses chamados Buckets do Oracle Cloud Infrastructure Object Storage. Os buckets fornecem capacidade de armazenamento abundante e várias opções para upload de arquivos, incluindo uma CLI, uma API REST e a console de administração.

Os dados transformados a serem carregados são semelhantes a este arquivo JSON:

"invoices": [ 
{ 
    "invoiceId": "222290", 
    "businessUnit": "US1 Business Unit", 
    "source": "External", 
    "invoiceNumber": "111190", 
    "invoiceAmount": "4242.00", 
    "invoiceDate" : "2019/02/01", 
    "supplierName": "Staffing Services", 
    "supplierNumber" : 1253, 
    "supplierSite" : "Staffing US1", 
    "invoiceCurrency": "USD", 
    "paymentCurrency": "USD", 
    "description" : "New Invoice from global Angels", 
    "importSet": "AP_Cloud_Demo", 
    "invoiceType": "STANDARD", 
    "paymentTerms": "Immediate", 
    "termsDate": "2019/02/01", 
    "accountingDate": "2019/02/01", 
    "paymentMethod": "CHECK", 
    "invoiceLines": [ 
                    { 
                        "amount": "200", 
                         "description" : "Invoice Line Description" 
                    }, 
                    { 
                        "amount": "300", 
                        "description" : "Invoice Line Description2", 
                        "invoiceQuantity": "10", 
                        "unitPrice": "5" 
                    }] 
}]

Isso é muito mais simples do que o zip de importação FBDI do formato nativo do Oracle Cloud ERP, que inclui dois arquivos CSV, semelhante a estes trechos CSV:

222284,US1 Business Unit,External,111184,4242.00,2019/02/01,Staffing
              Services,1253,Staffing US1,USD,USD,New Invoice from global
              Angels,AP_Cloud_Demo,STANDARD,,,,,,Immediate,2019/02/01,,,2019/02/01,CHECK,Standard,#NULL,,,,,,,,,,,#NULL,,,,,#NULL,#NULL,,,,21,#NULL,,,#NULL,#NULL,,,,#NULL,,,,,,,,,,,,,,,N,N,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,END 
222284,1,ITEM,200,,,,Invoice Line
              Description,,,,,,,,,,,,,N,,#NULL,2019/02/01,,,,,#NULL,,,,,,,,,,,,,,,,,#NULL,,,N,1,,,N,,,,,,,N,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,END

O restante desta solução também usará buckets adicionais que armazenarão o arquivo à medida que ele estiver sendo processado. À medida que o processo se move, o arquivo passa para o próximo bucket. Assim que o arquivo é carregado no Oracle Cloud ERP, o arquivo é renomeado para incluir o JOBID de carregamento de dados do ERP.
Veja a seguir a descrição da ilustração load-data-serverless-overview.png
Descrição da ilustração load-data-serverless-overview.png

  1. Crie buckets de armazenamento na sua instância do OCI.
  2. Faça upload do arquivo JSON resultante para um bucket de armazenamento usando o seguinte comando da CLI:
    oci os object put -ns mynamespace -bn JSONIncoming --name mysimpliedJSONInvoice.json --file mysimpliedJSONInvoice.json 

Criar uma Função de Transformação

Ao carregar dados no Oracle Fusion, uma etapa obrigatória é transformar os dados de entrada no formato CSV correto e, em seguida, compactar os arquivos em um único arquivo ZIP. Neste exemplo, demonstramos essa etapa de transformação aceitando uma estrutura de dados JSON simplificada que, em seguida, transformamos no formato CSV exigido pelo Oracle Cloud ERP. Depois disso, encapsulamos os arquivos em um único arquivo ZIP pronto para upload.

Ao fazer isso manualmente, normalmente você precisa fazer download de arquivos de Macro do Excel, que eles podem preencher e gerar os arquivos zip. (Consulte Explorar Mais para obter um link.) Em vez disso, você pode executar a transformação usando uma Função que executa algum código Python.



A função de transformação obtém os dados JSON do bucket JSON de Entrada, os transforma usando o código em um CSV, zips-os e, em seguida, os armazena em um bucket ZIP de Entrada. A função de transformação usa uma abordagem de modelo para gerar o arquivo CSV. A interação com arquivos do OCI é simples: você pode colocar e excluir objetos e, se precisar copiar arquivos grandes, poderá executar isso de forma assíncrona. Este exemplo usa arquivos pequenos, portanto, a cópia assíncrona não é usada aqui.

put_object_response = object_storage_client.put_object(namespace, param_processing_bucket_name, data_file_name + "_ERPJOBID_" + erp_job_id, data_file.data.content)

Criar uma Função de Carga

O carregamento de dados no Oracle Cloud ERP é simples. Para este exemplo, usamos a API REST padrão importBulkData.

Este é um trecho de código que mostra como você pode carregar dados usando a API REST requests de código aberto:

erp_payload = {
        "OperationName": "importBulkData",
        "DocumentContent": base64_encoded_file,
        "ContentType": "zip",
        "FileName": data_file_name,
        "JobName": jobname,
        "ParameterList": paramlist,
        "CallbackURL": param_fa_callback_url,
        "NotificationCode": "10"
    }
    logging.info(f'Sending file to erp with payload {erp_payload}')
    result = requests.post(
        url=param_erp_url,
        auth=param_erp_auth,
        headers={"Content-Type": JSON_CONTENT_TYPE},
        json=erp_payload
    )
 
    if result.status_code != 201:
        message = "Error " + str(result.status_code) + " occurred during upload. Message=" + str(result.content)
        raise FA_REST_Exception("Error " + message)

Sobre como Vincular Objetos Usando Eventos

Quando um arquivo é submetido a upload nos Buckets do Oracle Cloud Infrastructure Object Storage, é possível configurar o Bucket para emitir um evento quando as operações CRUD são executadas. Esse evento pode ser capturado pelo serviço Oracle Cloud Infrastructure Events.

A criação de regras com base em eventos de objeto é declarativa. Por exemplo, você pode criar uma regra que implemente lógica, como "se um novo arquivo for criado no bucket chamado INCOMING_JSON, chame a função sem servidor erp-transform". Você pode configurar um bucket para Emitir Eventos de Objeto como uma opção na seção Recursos da guia Informações do Bucket.



Veja um exemplo de regra baseada em um evento do Object Storage emitido pelo Bucket JSON de entrada:



Os eventos permitem que você vincule operações que acontecem a um Bucket de armazenamento (ou outros objetos) a chamadas de Função para processamento. A funcionalidade baseada em evento permite criar uma sequência de operações que são acionadas por eventos de maneira realmente dissociada.

Esta imagem mostra o novo fluxo operacional com eventos implementados:



Cada Bucket foi marcado para emitir um evento e o serviço de evento captura esse evento e chama o Oracle Function apropriado. Ao chamar a função, o serviço de evento passa um payload de evento que, por sua vez, chama a função. No payload do evento, há o tipo de evento emitido e, nesse caso, também o Bucket e o nome do arquivo.

Proteger Senhas Usando o Oracle Vault

A Função LoadToSaaS precisa ser capaz de autenticar com o Oracle Cloud ERP. A autenticação requer o nome de usuário e a senha para o usuário de integração. O serviço Oracle Cloud Infrastructure Vault fornece um local seguro para armazenar senhas criptografadas.

Você pode armazenar o nome de usuário como uma variável de Configuração do Serviço Functions, mas sua prática não segura para armazenar a senha lá. O Oracle OCI fornece o Oracle Cloud Infrastructure Vault como uma solução ideal. No código, você pode consultar o vault e extrair o segredo. Uma vez recuperado, você pode usar esse segredo para executar uma chamada REST autenticada no Oracle Cloud ERP. Observe que o OCID dessa chave secreta não será alterado se você atualizá-la, para que possa atualizar senhas com segurança sem violar a solução.



Na tela Detalhes do Vault, em Recursos, selecione Segredos para criar novos segredos.

O seguinte código Python de exemplo pode ser usado para extrair o segredo:

signer = oci.auth.signers.get_resource_principals_signer()
secret_client = oci.secrets.SecretsClient(config={}, signer=signer)
secret_response = secret_client.get_secret_bundle("ocid1.vaultsecret.oc1.phx.xxxxxxxx")
base64_secret_bytes = secret_response.data.secret_bundle_content.content.encode('ascii')
base64_message_bytes = base64.b64decode(base64_secret_bytes)
print("secret value is " + base64_message_bytes.decode('ascii'))

Criar uma Função de Callback

Quando os dados são carregados no Oracle Fusion Applications, um callback para o cliente é enviado com um payload de status. Você pode criar uma função de callback para receber essas informações.

Quando os dados são carregados no Oracle Cloud ERP, o serviço executa as seguintes etapas (simplificadas):

  1. Os dados são carregados no Repositório do Fusion UCM
  2. Uma tarefa ESS transfere o conteúdo do arquivo para as tabelas de integração de ERP
  3. Um job ESS importa dados para tabelas transacionais
  4. Um relatório é gerado, mostrando quais linhas foram inseridas no repositório UCM
  5. Um callback para o cliente é enviado com um payload de status

A função final chamada nesta solução de exemplo é aquela que implementa o lado do cliente ou o lado de recebimento desse callback do Oracle Cloud ERP. O callback é uma chamada HTTP com dados XML. O Oracle Functions não são pontos finais REST; portanto, para poder receber a chamada HTTP GET do Oracle Cloud ERP, você precisa fazer front-end da Função com o Gateway de API.

Como antes, essa é uma operação declarativa que envolve o mapeamento do ponto final e do URL do recurso para uma Função. No assistente de implantação Criar (ou Editar), especifique informações de rota na etapa Rotas. Esta imagem mostra um exemplo:



Essa função erp-callback é acionada quando o Oracle Cloud ERP emite um callback. A função decodifica o payload XML e extrai o JOBID e o Status. Com o JOBID, você pode determinar para qual arquivo no bucket de Processamento o evento foi e, em seguida, mover o arquivo do bucket de Processamento para o bucket de Sucesso ou Erro. É importante ressaltar que, nos callbacks de ERP, um job bem-sucedido não significa necessariamente que os dados foram carregados no Oracle Cloud ERP: eles podem ter sido dados duplicados, uma organização de negócios desconhecida etc. Um aprimoramento que você poderia implementar no padrão demonstrado aqui seria para esta Função de callback para fazer download do relatório do UCM e introspectá-lo para determinar se todas as linhas foram inseridas com sucesso.

Estenda a Solução Assinando Notificações

O serviço Oracle Cloud Infrastructure Notifications permite criar tópicos nos quais você pode publicar mensagens. Esses tópicos podem ter assinantes que ouvem uma mensagem e depois enviá-la para algum lugar.

Com o fluxo completo, você pode aproveitar os benefícios da integração com a OCI. Como o fluxo foi projetado como microsserviços, e você está usando serviços nativos como eventos, você pode aproveitar recursos e serviços adicionais, como o Oracle Cloud Infrastructure Notifications. Neste exemplo de código, as notificações são e-mails, mas o assinante pode ser outra Função, um canal PagerDuty ou algum outro mecanismo. A arquitetura solidamente acoplada permite ser estendida de várias maneiras. Por exemplo, você pode adicionar uma função que insere dados em um painel de controle do Grafana e a notificação pode chamar essa função com alguns dados que você deseja exibir no Grafana.

Por exemplo, aqui está um trecho de código de uma função auxiliar que demonstra como enviar uma notificação a um tópico (usando seu OCID):

def publish_ons_notification(topic_id, msg_title, msg_body):
    try:
        signer = oci.auth.signers.get_resource_principals_signer()
        logging.info("Publish notification, topic id" + topic_id)
        client = oci.ons.NotificationDataPlaneClient({}, signer=signer)
        msg = oci.ons.models.MessageDetails(title=msg_title, body=msg_body)
        client.publish_message(topic_id, msg)
    except oci.exceptions.ServiceError as serr:
        logging.critical(f'Exception sending notification {0} to OCI, is the OCID of the notification correct? {serr}')
    except Exception as err:
        logging.critical(f'Unknown exception occurred when sending notification, please see log {err}')