Sviluppa le tue funzioni
Per elaborare i dati, sono necessarie tre unità di codice indipendenti: una funzione di trasformazione, una funzione di caricamento e una funzione di callback.
Queste funzioni sono state implementate utilizzando Oracle Functions e scritte in Python. Oracle Functions è perfettamente adatto a questo job perché il caricamento dei dati potrebbe essere qualcosa che accade con una frequenza limitata (forse una o due volte all'ora o al giorno). L'utilizzo di Oracle Functions è vantaggioso perché la funzione viene chiamata solo quando esiste un'operazione da eseguire e una volta terminata l'elaborazione, si arresta. Inoltre, non è disponibile alcun sistema operativo, routing o altro server da gestire; si tratta di un'architettura serverless. Per questo esempio l'implementazione di Python è stata scelta rispetto ad altre opzioni di linguaggio perché è facilmente comprensibile ed eseguibile e non vi è un requisito rigoroso in termini di prestazioni per questo job di caricamento dati.
Le nostre tre funzioni sono:
- Funzione di trasformazione per la traduzione del file di dati da un formato JSON semplificato al file ZIP specifico di Oracle Cloud ERP
- Funzione di caricamento per il caricamento del file in Oracle Cloud ERP
- Funzione di callback per la gestione della risposta da Oracle Fusion
Ognuna di queste funzioni acquisirà i dati dal bucket di storage OCI, li elaborerà e poi li inserirà in un altro bucket.
Usa bucket di storage OCI
I dati trasformati da caricare sono simili a questo file 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"
}]
}]
È molto più semplice del formato nativo Oracle Cloud ERP del file zip di importazione FBDI che comprende due file CSV, come questi estratti 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
La parte restante di questa soluzione utilizzerà anche bucket aggiuntivi per memorizzare il file durante l'elaborazione. Durante lo spostamento del processo, il file viene spostato nel bucket successivo. Non appena il file viene caricato in Oracle Cloud ERP, il file viene rinominato in modo da includere il JOBID di caricamento dati ERP.
Descrizione dell'immagine load-data-serverless-overview.png
Crea una funzione di trasformazione
Quando si esegue questa operazione manualmente, in genere è necessario scaricare i file macro di Excel, che possono essere popolati e generare i file zip. Vedere Esplora altro per un collegamento. È invece possibile eseguire la trasformazione utilizzando una funzione che esegue un codice Python.
La funzione di trasformazione recupera i dati JSON dal bucket JSON in entrata, lo trasforma utilizzando il codice in un file CSV, lo comprime e lo memorizza in un bucket ZIP in entrata. La funzione di trasformazione utilizza un approccio modello per generare il file CSV. L'interazione con i file OCI è semplice: è possibile inserire ed eliminare gli oggetti e, se è necessario copiare file di grandi dimensioni, è possibile eseguirlo in modo asincrono. In questo esempio vengono utilizzati file di piccole dimensioni in modo che la copia asincrona non venga utilizzata qui.
put_object_response = object_storage_client.put_object(namespace, param_processing_bucket_name, data_file_name + "_ERPJOBID_" + erp_job_id, data_file.data.content)
Crea una funzione di caricamento
Il caricamento di dati in Oracle Cloud ERP è semplice. Per questo esempio viene utilizzata l'API REST standard importBulkData.
Ecco uno snippet di codice che mostra come caricare i dati mediante l'API REST requests open source:
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)Informazioni sul collegamento di oggetti mediante gli eventi
Quando un file viene caricato in bucket Oracle Cloud Infrastructure Object Storage, è possibile configurare il bucket per emettere un evento quando vengono eseguite le operazioni CRUD. Questo evento può essere acquisito dal servizio Oracle Cloud Infrastructure Events.
La creazione di regole basate sugli eventi oggetto è dichiarativa. Ad esempio, puoi creare una regola che implementa la logica "se un nuovo file viene creato nel bucket denominato INCOMING_JSON, quindi chiamare la funzione serverless erp-transform". È possibile configurare un bucket in Emetti eventi oggetto come opzione nella sezione Funzioni della scheda Informazioni bucket.
Di seguito è riportato un esempio di regola basata su un evento di storage degli oggetti emesso dal bucket JSON in entrata:
Gli eventi consentono di collegare le operazioni che si verificano in un bucket di memorizzazione (o in altri oggetti) alle chiamate di funzione per l'elaborazione. La funzionalità basata su eventi ti consente di creare una stringa di operazioni attivate dagli eventi in modo realmente disaccoppiato.
Questa immagine mostra il nuovo flusso operativo con gli eventi implementati:
Ogni bucket è stato contrassegnato per emettere un evento e il servizio eventi acquisisce questo evento e richiama la funzione Oracle appropriata. Quando si richiama la funzione, il servizio eventi passa un payload evento che a sua volta richiama la funzione. All'interno del payload evento è il tipo di evento emesso e, in questo caso, anche il bucket e il nome file.
Proteggi le password mediante Oracle Vault
La funzione LoadToSaaS deve essere in grado di eseguire l'autenticazione con Oracle Cloud ERP. L'autenticazione richiede il nome utente e la password per l'utente integrazione. Il servizio Oracle Cloud Infrastructure Vault fornisce un luogo sicuro in cui memorizzare le password cifrate.
È possibile memorizzare il nome utente come variabile di configurazione delle funzioni, ma non è una procedura sicura per memorizzare la password. Oracle OCI fornisce Oracle Cloud Infrastructure Vault come soluzione ideale. All'interno del codice, puoi eseguire una query sul vault ed estrarre il segreto. Una volta recuperato, puoi utilizzare questo segreto per eseguire una chiamata REST autenticata a Oracle Cloud ERP. Tenere presente che l'OCID di questa chiave segreta non cambia se viene aggiornata, in modo da poter aggiornare le password in modo sicuro senza violare la soluzione.
Nella schermata Dettagli vault, in Risorse, selezionare Secrets per creare nuovi segreti.
Il seguente codice Python di esempio può essere utilizzato per estrarre il segreto:
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'))
Crea una funzione di callback
Quando i dati vengono caricati in Oracle Fusion Applications, al client viene inviato un callback con payload di stato. È possibile creare una funzione di callback per ricevere queste informazioni.
Quando i dati vengono caricati in Oracle Cloud ERP, il servizio esegue i seguenti passi (semplificati):
- I dati vengono caricati nel repository UCM Fusion
- Un processo ESS trasferisce i contenuti dei file nelle tabelle di integrazione ERP
- Un processo ESS importa i dati in tabelle transazionali
- Viene generato un report che mostra le righe inserite nel repository UCM
- Un callback al client viene inviato con un payload di stato
La funzione finale che viene chiamata in questa soluzione di esempio è quella che implementa il lato client o il lato ricevente di questo callback da Oracle Cloud ERP. Il callback è una chiamata HTTP con dati XML. Oracle Functions non sono endpoint REST, pertanto per poter ricevere la chiamata GET HTTP da Oracle Cloud ERP è necessario eseguire il front-end della funzione con il gateway API.
Come in precedenza, si tratta di un'operazione dichiarativa che prevede il mapping dell'endpoint e dell'URL risorsa a una funzione. Nella procedura guidata Crea (o Modifica) distribuzione, immettere le informazioni di instradamento nel passo Instradamenti. Nell'immagine è riportato un esempio.
Questa funzione erp-callback viene attivata quando Oracle Cloud ERP emette una richiamata. La funzione decodifica il payload XML ed estrae il JOBID e lo stato. Con JOBID puoi determinare a quale file nel bucket di elaborazione si riferisce l'evento, quindi spostare il file dal bucket di elaborazione al bucket Riuscito o Errore. È importante notare che nei callback ERP un job riuscito non significa necessariamente che i dati siano stati caricati in Oracle Cloud ERP: potrebbero essere stati duplicati, un'organizzazione aziendale sconosciuta e così via. Un miglioramento che è possibile implementare al modello illustrato qui sarebbe che questa funzione di callback scarichi il report da UCM e lo esegua l'introspezione per determinare se tutte le righe sono state inserite correttamente.
Estendi la soluzione iscrivendoti alle notifiche
Il servizio Oracle Cloud Infrastructure Notifications consente di creare topics in cui pubblicare i messaggi. Questi argomenti possono includere sottoscrittori che ascoltano un messaggio e poi lo inviano da qualche parte.
Con il flusso completo puoi sfruttare i vantaggi dell'integrazione con OCI. Poiché il flusso è stato progettato come microservizi e si utilizzano servizi nativi come gli eventi, è possibile sfruttare funzionalità e servizi aggiuntivi come Oracle Cloud Infrastructure Notifications. In questo codice di esempio le notifiche sono e-mail, ma il sottoscrittore potrebbe invece essere un'altra funzione, un canale PagerDuty o un altro meccanismo. L'architettura accoppiata in modo approssimativo si presta ad essere estesa in molti modi. Ad esempio, è possibile aggiungere una funzione che inserisce i dati in un dashboard Grafana e la notifica potrebbe chiamare questa funzione con alcuni dati che si desidera visualizzare in Grafana.
Ad esempio, questo è uno snippet di codice di una funzione di supporto che dimostra come inviare una notifica a un argomento (utilizzando il relativo 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}')





