Showing posts with label Functions. Show all posts
Showing posts with label Functions. Show all posts

Tuesday, April 11, 2023

#961 OIC 23.04 New Features - OCI Functions Action

 


Before 23.04 one had to create a REST connection for OCI functions, now, thanks to a capability RPST  - Resource Principal authentication. 

The resource principal provider uses a resource provider session token (RPST) that enables the function to authenticate itself with other Oracle Cloud Infrastructure services. The token is only valid for the resources to which the dynamic group has been granted access. This requires one to use Identity Domains

Ergo, instead of having to create a connection, define security etc., we get access, based on policies granted to the OIC instance.

Essentially, you create dynamic group with a matching rule that includes your OIC ocid(s). e.g.

myOICDynGroup

Matching Rule: resource.id = myOIC clientId

You now create a policy leveraging the above - 

allow dynamic-group myOICDynGroup to manage functions-family in compartment myFnCompartment 






So where to find the OIC instance client id?




Finally, I create the policy - 


So now, we know how to do the pre-requisites, let's kick the tyres -

My simple integration above invokes a simple python based function. Here's the actual code - 









import io
import json
import logging
from fdk import response

def handler(ctx, data: io.BytesIO = None):
    discount = 5
    try:
        body = json.loads(data.getvalue())
        product = body.get("product")
        unitsOrdered = body.get("unitsOrdered")

    except (Exception, ValueError) as ex:
        logging.getLogger().info('error parsing json payload: ' + str(ex))
try:
        body = json.loads(data.getvalue())
        product = body.get("product")
        unitsOrdered = body.get("unitsOrdered")
    except (Exception, ValueError) as ex:
        logging.getLogger().info('error parsing json payload: ' + str(ex))
    logging.getLogger().info("Inside Python order discount function")
    if product == "iBike" and int(unitsOrdered) > 30:
       discount = 10
    else:
       discount = 5
    return response.Response(
        ctx, response_data=json.dumps(
            {"product": product,  
"discount": discount}),
             headers={"Content-Type": "application/json"}
    )
The request format is as follows - 
{"product": "someProduct",
"unitsOrdered": 10}

The response format is as follows - 
 {"product": product,  
   "discount": 10}

This information will be required, when using the new OCI Functions action in OIC.
Naturally, we don't expect the OIC developer to have to delve into the function code to get this data, usually the functions developers will publish this somewhere, for example, in a confluence page.

The OCI Functions Service, like all other OCI services is provisioned in a compartment, within a region.


The actual functions are contained within an application - here is the orderDiscount function I will invoke -

Now to OIC - 


I configure as follows - 









Now to testing - 


Only 10% discount on 99 iBikes? Just shows how popular they are.

Monday, January 17, 2022

#896 OIC invoking OCI Functions (python) - Part 1 Creating the python function



OIC is part of the OCI family, a family of rich services that can be leveraged from your integrations and processes. This post shows how easy it is to create a python function in OCI Functions. 

The next post will cover leveraging this python function from OIC.

So to the simple use case - product discounts - the python function will apply these for me. I know it is a banal example but aren't we all experts at extrapolating?

Starting point is a simple python function I have created.  







So how do we expose this logic via OCI Functions?

Step 1 - Create a new Application in OCI Functions




Step 2- Click on Cloud Shell (Getting Started) and create the function


















Now I will create a python based function within this application - first step - list my current apps - 
fn list apps - there's my app - NiallCPythonApp -









Then I create a new default python function - 

fn init --runtime python niallCPythonDemo






Check out what's been generated - 






Let's look at the generated function code - 

















This is a simple helloWorld example, I will modify to include the discount functionality I mentioned at the outset - 
the business logic is simple - If the product is iBike and the quantity ordered is > 30 then 
apply a 10% discount; all other others get a 5% discount.

Here is the revised code - first I make a copy of the default function -





then I edit as follows - 




I delete func.py and then edit the yaml file to refer to the new function  - 








Step 3 - Deploy and Test

fn -v deploy --app NiallCPythonApp -












Function has been deployed - 























Now to testing this - method is as follows, when argument passing is required - 












so my cmd is as follows - 

echo -n '{"product":"iBike", "unitsOrdered":40}' | fn invoke NiallCPythonApp niallcpythondemo






Simple and succinct!
















Monday, December 21, 2020

#819 Invoking Oracle Functions from OIC


 

Introduction

From the Oracle website -

Oracle Cloud Functions is a serverless platform that lets developers create, run, and scale applications without managing any infrastructure. Functions integrate with Oracle Cloud Infrastructure, platform services and SaaS applications. Because Functions is based on the open source Fn Project, developers can create applications that can be easily ported to other cloud and on-premises environments. Code based on Functions typically runs for short durations, and customers pay only for the resources they use.

These Functions can be written in a variety of languages - Java, Python, Node etc. You write and deploy the code, Oracle takes care of provisioning, scaling etc.

Think of blocks of code that generally do one thing, e.g. applying a discount to an order. These are stored as Docker images in a docker registry. They can be executed via CLI or HTTP request. We will be using the latter in our example.


So how do Functions enhance the OIC experience? 

Here's one use case - Say you are porting SOA Composites to OIC and you make use of Java Embedding in your BPEL process. Where shall I put that code in OIC?

Net, net - Functions can be leveraged to implement business logic that cannot be defined using the standard OIC actions. I realise that we do have the ability to upload Javascript libraries to OIC and then call the functions within, from an OIC process; however, that also has its limitations.

So let's implement a simple example - 

Here is my Java class -







I realise the above is banal, but we can all extrapolate, correct?

Functions: creating an Application

Starting point is access to Oracle Functions










Here I create an application - So what is an application?

Essentially a bucket for your functions e.g. HCM Functions, ERP Functions etc. It is also a unit at which resources can be allocated and configured. These resources can include subnet(s) allocated, whether logging is enabled etc.

.














Functions: creating a Function

I click on Getting Started






















Launch Cloud Shell - 










Note the initial setup cmds - you only need to do this once.

I do a fn list apps command and see my 3 apps, including oic-discount-app.

I now enter a cmd to create a Java function skeleton - fn init --runtime java oicdiscount-java




So what has been generated?





Let's look at the /src directory structure -




Let's look at main -





So here it has generated an example Hello World java








I make a copy of this file, calling it OICDiscountFunction.java

I vi the result -











I can now delete the HelloWorld function -




So now I have my Java code defined, what about the other directory - /test?
















As you can see, a tester class that invokes HelloWorld.
I could get remove the /test directory, but for the moment, let's amend to call our OICDiscountFunction. Same procedure- copy the existing Java file and amend as necessary.













Now I can remove the Hello tester -





Now back up to the top app directory -




I need to amend both these files, as they reference the HelloWorld demo.

func.yaml 

As you can see here, I will need to change the cmd line.








to 







pom.xml

All I need to change here is artifactId












This I change to oicdiscount
I could also add a property here to skip testing - 








Thanks - Angelo Santagata - for pointing this out to me.

Now to deployment - 



















Validate it was successful - 



























I activate logging at Application level -








































Calling Functions from OIC

My colleague Daniel Teixeira wrote a great blog post which helped me out. you can read it here  

Step 1 is to create a REST Connection to OCI Functions -












So what will we need here?
1. base url
2. tenancy ocid
3. user ocid
4. private key
5. fingerprint

3 -5 can be found here -


 
























API Keys can be created/uploaded here - 




















As you can see, the Fingerprint is also available here.

What about the base url?

Check out the Functions screen again - note the Invoke Endpoint url 



take that url up to, but not including /20181201

For Security - we use OCI Signature Version 1

For Tenancy OCID - 























I enter all the required fields - then Save and Test the REST Connection -












I create an App-Driven Orchestration in OIC - 

Rest Trigger - Request -














Response - 

















I drop the OCI Functions REST Connection and configure as follows -

relative resource uri will be set to /2018.../invoke from the Invoke Endpoint.











Naturally I could do this somewhat more elegantly - but this is a simple demo, isn't it?























Request is defined as follows - 



















Use the same definition for the Response.

Now to the Mapping - 












Source is the country from the Request -

Mapping will use the following OIC Advanced Mapping Functions -

oraext:decodeBase64ToReference (
oraext:encodeBase64 (/nssrcmpr:execute/nssrcdfl:request-wrapper/nssrcdfl:country ) )

















Response Mapping - essentially we do the opposite - 

oraext:decodeBase64 (oraext:encodeReferenceToBase64 ($ComputeDiscount/nsmpr0:executeResponse/ns25:streamReference ) )

















Deploy and Test - 


























I can check out the OCI Logs - 














A simple and effective combination - OIC and Oracle Functions.

Happy Christmas 2020 and may 2021 bring you more  Health, Wealth and Happiness.