Desarrollo de funciones
Para procesar los datos, necesitará tres unidades de código independientes: una función de transformación, una función de carga y una función de devolución de llamada.
Estas funciones se han implantado mediante Oracle Functions y se han escrito en Python. Oracle Functions se ajusta perfectamente a este trabajo porque la carga de datos podría ser algo que sucede con una frecuencia limitada (quizás una o dos veces por hora o por día). El uso de Oracle Functions es ventajoso porque la función solo se llama cuando hay algo que hacer y una vez que ha terminado el procesamiento, se cierra. Además, no hay ningún sistema operativo, enrutamiento u otro servidor que pueda mantener; se trata de una arquitectura sin servidor. Para esta implementación de ejemplo, elegimos Python en lugar de otras opciones de lenguaje porque es fácil de entender y exendible, y no hay un requisito de rendimiento estricto para este trabajo de carga de datos.
Nuestras tres funciones son:
- Función de transformación para traducir el archivo de datos de un formato JSON simplificado al archivo zip específico de Oracle Cloud ERP
- Función de carga para cargar el archivo en Oracle Cloud ERP
- Una función de devolución de llamada para manejar la respuesta de Oracle Fusion
Cada una de estas funciones obtendrá sus datos del cubo de OCI Storage, los procesará y, a continuación, los colocará en otro cubo.
Usar cubos de almacenamiento de OCI
Los datos transformados que se van a cargar son similares a este archivo 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"
}]
}]
Esto es mucho más sencillo que el zip de importación de FBDI con formato nativo de Oracle Cloud ERP, que consta de dos archivos CSV, similares a estos extractos 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
El resto de esta solución también utilizará cubos adicionales para almacenar el archivo a medida que se procesa. A medida que el proceso avanza, el archivo pasa al siguiente cubo. Tan pronto como el archivo se carga en Oracle Cloud ERP, el nombre del archivo se cambia para incluir el JOBID de carga de datos de ERP.
Descripción de la ilustración load-data-serverless-overview.png
Crear una función de transformación
Al hacerlo manualmente, normalmente necesita descargar los archivos Macro de Excel, que pueden rellenar y generar los archivos zip. (Consulte Explorar más para ver un enlace.) En su lugar, puede realizar la transformación mediante una función que ejecute código Python.
La función de transformación obtiene los datos JSON del cubo de JSON entrante, los transforma mediante código en CSV, los comprime y, a continuación, los almacena en un cubo de ZIP entrante. La función de transformación utiliza un enfoque de plantilla para generar el archivo CSV. Interactuar con archivos OCI es sencillo: puede poner y suprimir objetos y, si necesita copiar archivos grandes, puede ejecutarlos de forma asíncrona. Este ejemplo utiliza archivos pequeños para que no se utilice aquí la copia asíncrona.
put_object_response = object_storage_client.put_object(namespace, param_processing_bucket_name, data_file_name + "_ERPJOBID_" + erp_job_id, data_file.data.content)
Crear una función de carga
La carga de datos en Oracle Cloud ERP es sencilla. En este ejemplo, utilizamos la API de REST importBulkData
estándar.
A continuación, se muestra un fragmento de código que muestra cómo cargar datos mediante la API de REST de código abierto requests
:
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)
Acerca del enlace conjunto de objetos mediante eventos
Cuando se carga un archivo en cubos de Oracle Cloud Infrastructure Object Storage, es posible configurar el cubo para que emita un evento cuando se realizan operaciones CRUD. El servicio Oracle Cloud Infrastructure Events puede capturar este evento.
La creación de reglas basadas en eventos de objeto es declarativa. Por ejemplo, puede crear una regla que implante una lógica como "si se crea un nuevo archivo en un cubo denominado INCOMING_JSON
, llame a la función sin servidor erp-transform
". Puede configurar un cubo para Emitir eventos de objeto como opción en la sección Funciones del separador Información de cubo.
A continuación, se muestra un ejemplo de una regla basada en un evento de Object Storage emitido por el cubo de JSON entrante:
Los eventos le permiten vincular operaciones que tienen lugar en un cubo de almacenamiento (u otros objetos) a llamadas de función para su procesamiento. La funcionalidad basada en eventos permite crear una cadena de operaciones que se disparan por eventos de una forma realmente desacoplada.
En esta imagen se muestra el nuevo flujo operativo con eventos implementados:
Cada cubo se ha marcado para emitir un evento y el servicio de eventos captura este evento y llama a la función de Oracle adecuada. Al llamar a la función, el servicio de eventos pasa una carga útil de eventos que, a su vez, llama a la función. Dentro de la carga útil del evento se encuentra el tipo de evento emitido y, en este caso, también el cubo y el nombre de archivo.
Proteger contraseñas con Oracle Vault
La función LoadToSaaS
debe poder autenticarse con Oracle Cloud ERP. La autenticación requiere el nombre de usuario y la contraseña del usuario de integración. El servicio Oracle Cloud Infrastructure Vault proporciona un lugar seguro para almacenar contraseñas cifradas.
Puede almacenar el nombre de usuario como una variable de configuración de Functions, pero su práctica no segura de almacenar la contraseña allí. Oracle OCI proporciona Oracle Cloud Infrastructure Vault como una solución ideal. Dentro del código puede consultar el almacén y extraer el secreto. Una vez recuperado, puede utilizar este secreto para ejecutar una llamada REST autenticada en Oracle Cloud ERP. Tenga en cuenta que el OCID de esta clave secreta no cambia si la actualiza, por lo que puede actualizar contraseñas de forma segura sin interrumpir la solución.
En la pantalla Detalles de almacén, en Recursos, seleccione Secretos para crear nuevos secretos.
El siguiente código Python de ejemplo se puede utilizar para extraer el secreto:
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'))
Crear una función de devolución de llamada
Cuando se cargan datos en Oracle Fusion Applications, se envía una devolución de llamada al cliente con una carga útil de estado. Puede crear una función de devolución de llamada para recibir esta información.
Cuando se cargan datos en Oracle Cloud ERP, el servicio realiza los siguientes pasos (simplificados):
- Los datos se cargan en el repositorio de Fusion UCM
- Una tarea de ESS transfiere el contenido del archivo a las tablas de integración de ERP.
- Una tarea de ESS importa datos a tablas transaccionales
- Se genera un informe que muestra las filas insertadas en el repositorio de UCM
- Se envía una devolución de llamada al cliente con una carga útil de estado
La función final a la que se llama en esta solución de ejemplo es la que implanta el cliente o el lado de recepción de esta devolución de llamada de Oracle Cloud ERP. La devolución de llamada es una llamada HTTP con datos XML. Oracle Functions no son puntos finales REST, por lo que para poder recibir la llamada GET HTTP de Oracle Cloud ERP debe recibir front-end de la función con API Gateway.
Como antes, se trata de una operación declarativa que implica la asignación del punto final y la URL del recurso a una función. En el asistente de despliegue Crear (o Editar), introduzca la información de ruta en el paso Rutas. En esta imagen se muestra un ejemplo:
Esta función erp-callback
se dispara cuando Oracle Cloud ERP emite una devolución de llamada. La función descodifica la carga útil XML y extrae el JOBID y el estado. Con JOBID puede determinar a qué archivo del cubo de procesamiento corresponde el evento y, a continuación, mover el archivo del cubo de procesamiento al cubo Correcto o Error. Es importante destacar que, en las devoluciones de llamada de ERP, un trabajo correcto no significa necesariamente que los datos se hayan cargado en Oracle Cloud ERP: puede haber sido datos duplicados, una organización de negocio desconocida, etc. Una mejora que podría implementar en el patrón que se muestra aquí sería para esta función de devolución de llamada descargar el informe de UCM e inspeccionarlo para determinar si todas las filas se insertaron correctamente.
Ampliación de la solución suscribiéndose al servicio de notificaciones
El servicio Oracle Cloud Infrastructure Notifications permite crear temas donde puede publicar mensajes. Estos temas pueden tener suscriptores que escuchan un mensaje y, a continuación, lo envían a otro lugar.
Con el flujo completo puede aprovechar las ventajas de la integración con OCI. Dado que el flujo se ha diseñado como microservicios, y que utiliza servicios nativos como eventos, puede aprovechar funciones y servicios adicionales como Oracle Cloud Infrastructure Notifications. En este código de ejemplo, las notificaciones son correos electrónicos, pero el suscriptor podría ser otra función, un canal PagerDuty o algún otro mecanismo. La arquitectura débilmente acoplada se presta a extenderse de muchas maneras. Por ejemplo, puede agregar una función que inserte datos en un panel de control de Grafana y la notificación podría llamar a esta función con algunos datos que desea mostrar en Grafana.
Por ejemplo, aquí hay un fragmento de código de una función auxiliar que demuestra cómo enviar una notificación a un tema (mediante su 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}')