├── hello-python ├── requirements.txt ├── func.yaml └── func.py ├── request-info ├── requirements.txt ├── func.yaml └── func.py ├── vault-secret ├── requirements.txt ├── func.yaml └── func.py ├── custom-docker ├── image1.jpg ├── image2.jpg ├── func.yaml ├── Dockerfile ├── package.json └── func.js ├── event-fn ├── oci-events-image.png ├── oci-functions-image.png ├── func.yaml ├── src │ └── main │ │ └── java │ │ └── com │ │ └── example │ │ └── fn │ │ └── HandleImageMetadata.java ├── example-image-metadata.json └── pom.xml ├── hello-node-test ├── func.yaml ├── __mocks__ │ └── @fnproject │ │ └── fdk.js ├── package.json ├── func.js └── func.test.js ├── create-file ├── func.yaml ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── example │ └── fn │ └── WriteFileProduceMessage.java ├── README.md └── API-specs └── petstoreAPIv3.yaml /hello-python/requirements.txt: -------------------------------------------------------------------------------- 1 | fdk 2 | -------------------------------------------------------------------------------- /request-info/requirements.txt: -------------------------------------------------------------------------------- 1 | fdk 2 | oci -------------------------------------------------------------------------------- /vault-secret/requirements.txt: -------------------------------------------------------------------------------- 1 | fdk 2 | oci 3 | -------------------------------------------------------------------------------- /custom-docker/image1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ou-developers/oci-functions/main/custom-docker/image1.jpg -------------------------------------------------------------------------------- /custom-docker/image2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ou-developers/oci-functions/main/custom-docker/image2.jpg -------------------------------------------------------------------------------- /event-fn/oci-events-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ou-developers/oci-functions/main/event-fn/oci-events-image.png -------------------------------------------------------------------------------- /event-fn/oci-functions-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ou-developers/oci-functions/main/event-fn/oci-functions-image.png -------------------------------------------------------------------------------- /vault-secret/func.yaml: -------------------------------------------------------------------------------- 1 | schema_version: 20180708 2 | name: vault-secret 3 | version: 0.0.12 4 | runtime: python 5 | entrypoint: /python/bin/fdk /function/func.py handler 6 | memory: 256 -------------------------------------------------------------------------------- /custom-docker/func.yaml: -------------------------------------------------------------------------------- 1 | schema_version: 20180708 2 | name: custom-docker 3 | version: 0.0.19 4 | runtime: docker 5 | triggers: 6 | - name: custom-docker-trigger 7 | type: http 8 | source: /custom-docker 9 | -------------------------------------------------------------------------------- /hello-node-test/func.yaml: -------------------------------------------------------------------------------- 1 | schema_version: 20180708 2 | name: hello-node-test 3 | version: 0.0.2 4 | runtime: node 5 | build_image: fnproject/node:14-dev 6 | run_image: fnproject/node:14 7 | entrypoint: node func.js 8 | -------------------------------------------------------------------------------- /hello-python/func.yaml: -------------------------------------------------------------------------------- 1 | schema_version: 20180708 2 | name: hello-python 3 | version: 0.0.5 4 | runtime: python 5 | build_image: fnproject/python:3.9-dev 6 | run_image: fnproject/python:3.9 7 | entrypoint: /python/bin/fdk /function/func.py handler 8 | memory: 256 9 | -------------------------------------------------------------------------------- /request-info/func.yaml: -------------------------------------------------------------------------------- 1 | schema_version: 20180708 2 | name: request-info 3 | version: 0.0.10 4 | runtime: python 5 | build_image: fnproject/python:3.9-dev 6 | run_image: fnproject/python:3.9 7 | entrypoint: /python/bin/fdk /function/func.py handler 8 | memory: 256 9 | -------------------------------------------------------------------------------- /event-fn/func.yaml: -------------------------------------------------------------------------------- 1 | schema_version: 20180708 2 | name: event-fn 3 | version: 0.0.5 4 | runtime: java 5 | build_image: fnproject/fn-java-fdk-build:jdk17-1.0.151 6 | run_image: fnproject/fn-java-fdk:jre17-1.0.151 7 | cmd: com.example.fn.HandleImageMetadata::handleRequest 8 | -------------------------------------------------------------------------------- /create-file/func.yaml: -------------------------------------------------------------------------------- 1 | schema_version: 20180708 2 | name: create-file 3 | version: 0.0.24 4 | runtime: java 5 | build_image: fnproject/fn-java-fdk-build:jdk11-1.0.130 6 | run_image: fnproject/fn-java-fdk:jre11-1.0.130 7 | cmd: com.example.fn.WriteFileProduceMessage::handle 8 | timeout: 120 9 | -------------------------------------------------------------------------------- /hello-node-test/__mocks__/@fnproject/fdk.js: -------------------------------------------------------------------------------- 1 | const handle = function (f) { 2 | theFunction = f 3 | return f 4 | } 5 | let theFunction 6 | const functionCache = function getFunction() { 7 | return theFunction 8 | } 9 | exports.handle = handle 10 | exports.functionCache = functionCache -------------------------------------------------------------------------------- /custom-docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM fnproject/node:dev as build-stage 2 | WORKDIR /function 3 | ADD package.json /function/ 4 | RUN npm install 5 | 6 | FROM fnproject/node 7 | WORKDIR /function 8 | ADD . /function/ 9 | COPY --from=build-stage /function/node_modules/ /function/node_modules/ 10 | ENTRYPOINT ["node", "func.js"] 11 | -------------------------------------------------------------------------------- /custom-docker/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "custom-docker", 3 | "version": "1.0.0", 4 | "description": "Function using ImageMagick that returns dimensions", 5 | "main": "func.js", 6 | "author": "fnproject.io", 7 | "license": "Apache-2.0", 8 | "dependencies": { 9 | "@fnproject/fdk": ">=0.0.11", 10 | "tmp": "^0.0.33", 11 | "imagemagick": "^0.1.3" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /hello-node-test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hellofn", 3 | "version": "1.0.0", 4 | "description": "example function", 5 | "main": "func.js", 6 | "author": "", 7 | "license": "Apache-2.0", 8 | "dependencies": { 9 | "@fnproject/fdk": ">=0.0.41" 10 | }, 11 | "devDependencies": { 12 | "jest": "^29.1.2" 13 | }, 14 | "scripts": { 15 | "test": "jest" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /hello-node-test/func.js: -------------------------------------------------------------------------------- 1 | const fdk=require('@fnproject/fdk'); 2 | 3 | fdk.handle(function(input){ 4 | // To pass both tests, remove or comment the line above and uncomment the line below 5 | //fdk.handle(function(input, context){ // adding context parameter 6 | 7 | let name = 'World'; 8 | if (input.name) { 9 | name = input.name; 10 | } 11 | console.log('\nInside Node Hello World function') 12 | 13 | return {'message': 'Hello ' + name} 14 | // To pass both tests, remove or comment the line above and uncomment the line below 15 | //return {'message': 'Hello ' + name, "ctx":context} // return the context object 16 | 17 | }) 18 | -------------------------------------------------------------------------------- /custom-docker/func.js: -------------------------------------------------------------------------------- 1 | const fdk = require('@fnproject/fdk'); 2 | const fs = require('fs'); 3 | const tmp = require('tmp'); 4 | const im = require('imagemagick'); 5 | 6 | fdk.handle((buffer, ctx) => { 7 | return new Promise((resolve, reject) => { 8 | tmp.tmpName((err, tmpFile) => { 9 | if (err) throw err; 10 | fs.writeFile(tmpFile, buffer, (err) => { 11 | if (err) throw err; 12 | im.identify(['-format', '{"width": %w, "height": %h}', tmpFile], 13 | (err, output) => { 14 | if (err) { 15 | reject(err); 16 | } else { 17 | resolve(JSON.parse(output)); 18 | } 19 | } 20 | ); 21 | }); 22 | }); 23 | }); 24 | }, { inputMode: 'buffer' }); 25 | -------------------------------------------------------------------------------- /hello-node-test/func.test.js: -------------------------------------------------------------------------------- 1 | // requires func.js registers the function (input, context) with mock fdk 2 | const func = require( './func.js' ); 3 | const fdk=require('@fnproject/fdk'); 4 | 5 | // setting value for 1st parameter 6 | const name ="Your Name" 7 | const input = {"name":name} 8 | 9 | //setting value for 2nd parameter 10 | const context = {"_headers":{"Host":"localhost","Content-Type":"application/json"}} 11 | 12 | // get the function that was registered in func.js with the (mock) fdk handler 13 | const theFunction = fdk.functionCache() 14 | 15 | // execute 1st test: Expecting "Hello " 16 | test(`Test of func.js for ${name}`, () => { 17 | expect(theFunction(input,context).message) 18 | .toBe(`Hello ${name}`); 19 | }); 20 | 21 | // execute 2nd test: Expecting the context object 22 | test(`Test of func.js for Host header in context object`, () => { 23 | expect(theFunction(input,context).ctx._headers.Host) 24 | .toBe(`localhost`); 25 | }); -------------------------------------------------------------------------------- /hello-python/func.py: -------------------------------------------------------------------------------- 1 | # 2 | # hello-python version 1.1 3 | # 4 | # Copyright (c) 2022 Oracle, Inc. All rights reserved. 5 | # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. 6 | # 7 | 8 | import io 9 | import json 10 | 11 | from fdk import response 12 | 13 | 14 | def handler(ctx, data: io.BytesIO=None): 15 | print("Entering Python Hello World handler", flush=True) 16 | name = "World" 17 | try: 18 | body = json.loads(data.getvalue()) 19 | name = body.get("name") 20 | except (Exception, ValueError) as ex: 21 | print(str(ex), flush=True) 22 | 23 | print("Value of name = ", name, flush=True) 24 | print("Exiting Python Hello World handler", flush=True) 25 | return response.Response( 26 | ctx, response_data=json.dumps( 27 | {"message": "Hello {0}".format(name)}), 28 | headers={"Content-Type": "application/json"} 29 | ) 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # oci-functions 2 | OCI Fn functions and resources used for hands-on lab practices in the OCI Developer Professional course 3 | 4 | hello-python: A simple Python Hello World function - used for Lab 06 & Lab 08 5 | 6 | custom-docker: A Node function that requires editing a custom Dockerfile - used for Lab 07 7 | 8 | request-info: A Python function that returns HTTP request info and function info - used for Lab 09 9 | 10 | API-specs: A folder containing an OpenAPIv3.0 description for the PetStore API - used for Lab 10 11 | 12 | create-file: A Java function that writes to an OCI Stream and Object Storage bucket - used for Lab 11 13 | 14 | event-fn: A Java function that processes image metadata when triggered by a CloudEvent - used for Lab 12 15 | 16 | hello-node-test: A Node Hello World function setup for testing with Jest - used for Lab 13 17 | 18 | vault-secret: A Python function that retrieves a secret from an OCI Vault - used for Lab 14 19 | -------------------------------------------------------------------------------- /event-fn/src/main/java/com/example/fn/HandleImageMetadata.java: -------------------------------------------------------------------------------- 1 | package com.example.fn; 2 | 3 | import com.drew.imaging.ImageMetadataReader; 4 | import com.drew.imaging.ImageProcessingException; 5 | import com.drew.metadata.Metadata; 6 | import com.fasterxml.jackson.databind.ObjectMapper; 7 | import io.cloudevents.CloudEvent; 8 | 9 | import java.io.IOException; 10 | import java.io.InputStream; 11 | import java.net.URL; 12 | import java.util.Map; 13 | 14 | public class HandleImageMetadata { 15 | 16 | public Metadata handleRequest(CloudEvent event) throws IOException, ImageProcessingException { 17 | ObjectMapper objectMapper = new ObjectMapper(); 18 | Map data = objectMapper.convertValue(event.getData().get(), Map.class); 19 | Map additionalDetails = objectMapper.convertValue(data.get("additionalDetails"), Map.class); 20 | 21 | String region = System.getenv("OCI_RESOURCE_PRINCIPAL_REGION"); 22 | 23 | String imageUrl = "https://objectstorage." + region + ".oraclecloud.com/n/" + 24 | additionalDetails.get("namespace") + "/b/" + additionalDetails.get("bucketName") + 25 | "/o/" + data.get("resourceName"); 26 | 27 | InputStream imageStream = new URL(imageUrl).openStream(); 28 | Metadata metadata = ImageMetadataReader.readMetadata(imageStream); 29 | System.out.println(objectMapper.writeValueAsString(metadata)); 30 | 31 | //todo: do something with the metadata 32 | 33 | return metadata; 34 | } 35 | 36 | } -------------------------------------------------------------------------------- /event-fn/example-image-metadata.json: -------------------------------------------------------------------------------- 1 | {"directories":[{"name":"PNG-IHDR","pngChunkType":{"private":true,"critical":true,"ancillary":false,"safeToCopy":false,"identifier":"IHDR"},"empty":false,"parent":null,"tags":[{"description":"210","tagType":1,"tagName":"Image Width","tagTypeHex":"0x0001","directoryName":"PNG-IHDR"},{"description":"215","tagType":2,"tagName":"Image Height","tagTypeHex":"0x0002","directoryName":"PNG-IHDR"},{"description":"8","tagType":3,"tagName":"Bits Per Sample","tagTypeHex":"0x0003","directoryName":"PNG-IHDR"},{"description":"True Color with Alpha","tagType":4,"tagName":"Color Type","tagTypeHex":"0x0004","directoryName":"PNG-IHDR"},{"description":"Deflate","tagType":5,"tagName":"Compression Type","tagTypeHex":"0x0005","directoryName":"PNG-IHDR"},{"description":"Adaptive","tagType":6,"tagName":"Filter Method","tagTypeHex":"0x0006","directoryName":"PNG-IHDR"},{"description":"No Interlace","tagType":7,"tagName":"Interlace Method","tagTypeHex":"0x0007","directoryName":"PNG-IHDR"}],"errors":[],"tagCount":7,"errorCount":0},{"name":"XMP","xmpmeta":{"packetHeader":null,"root":{"name":null,"value":null,"parent":null,"options":{"options":0,"arrayElementsLimit":-1,"array":false,"hasLanguage":false,"uri":false,"arrayOrdered":false,"arrayAltText":false,"hasType":false,"struct":false,"arrayLimited":false,"schemaNode":false,"onlyArrayOptions":true,"simple":true,"arrayAlternate":false,"hasQualifiers":false,"qualifier":false,"compositeProperty":false,"optionsString":""},"implicit":false,"hasAliases":false,"alias":false,"hasValueChild":false,"childrenLength":0,"qualifierLength":0,"unmodifiableChildren":[]},"objectName":""},"xmpProperties":{},"empty":false,"parent":null,"tags":[],"errors":["Error processing XMP data: XML parsing failure"],"tagCount":0,"errorCount":1},{"name":"PNG-tEXt","pngChunkType":{"private":true,"critical":false,"ancillary":true,"safeToCopy":true,"identifier":"tEXt"},"empty":false,"parent":null,"tags":[{"description":"Software: Adobe ImageReady","tagType":13,"tagName":"Textual Data","tagTypeHex":"0x000d","directoryName":"PNG-tEXt"}],"errors":[],"tagCount":1,"errorCount":0},{"name":"File Type","empty":false,"parent":null,"tags":[{"description":"PNG","tagType":1,"tagName":"Detected File Type Name","tagTypeHex":"0x0001","directoryName":"File Type"},{"description":"Portable Network Graphics","tagType":2,"tagName":"Detected File Type Long Name","tagTypeHex":"0x0002","directoryName":"File Type"},{"description":"image/png","tagType":3,"tagName":"Detected MIME Type","tagTypeHex":"0x0003","directoryName":"File Type"},{"description":"png","tagType":4,"tagName":"Expected File Name Extension","tagTypeHex":"0x0004","directoryName":"File Type"}],"errors":[],"tagCount":4,"errorCount":0}],"directoryCount":4} -------------------------------------------------------------------------------- /event-fn/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | UTF-8 8 | 1.0.151 9 | 10 | com.example.fn 11 | event-fun 12 | 1.0.0 13 | 14 | 15 | 16 | com.fnproject.fn 17 | api 18 | ${fdk.version} 19 | 20 | 21 | com.fnproject.fn 22 | testing-core 23 | ${fdk.version} 24 | test 25 | 26 | 27 | com.fnproject.fn 28 | testing-junit4 29 | ${fdk.version} 30 | test 31 | 32 | 33 | junit 34 | junit 35 | 4.12 36 | test 37 | 38 | 39 | io.cloudevents 40 | cloudevents-api 41 | 0.2.1 42 | 43 | 44 | com.drewnoakes 45 | metadata-extractor 46 | 2.12.0 47 | 48 | 49 | 50 | 51 | 52 | 53 | org.apache.maven.plugins 54 | maven-compiler-plugin 55 | 3.3 56 | 57 | 17 58 | 17 59 | 60 | 61 | 62 | org.apache.maven.plugins 63 | maven-surefire-plugin 64 | 2.22.1 65 | 66 | false 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /vault-secret/func.py: -------------------------------------------------------------------------------- 1 | # 2 | # vault-secret version 1.0. 3 | # 4 | # Copyright (c) 2020 Oracle, Inc. 5 | # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. 6 | # 7 | 8 | import io 9 | import json 10 | import base64 11 | import oci 12 | import logging 13 | import hashlib 14 | 15 | from fdk import response 16 | 17 | def get_text_secret(secret_ocid): 18 | #decrypted_secret_content = "" 19 | signer = oci.auth.signers.get_resource_principals_signer() 20 | try: 21 | client = oci.secrets.SecretsClient({}, signer=signer) 22 | secret_content = client.get_secret_bundle(secret_ocid).data.secret_bundle_content.content.encode('utf-8') 23 | decrypted_secret_content = base64.b64decode(secret_content).decode("utf-8") 24 | except Exception as ex: 25 | print("ERROR: failed to retrieve the secret content", ex, flush=True) 26 | raise 27 | return {"secret content": decrypted_secret_content} 28 | 29 | 30 | def get_binary_secret_into_file(secret_ocid, filepath): 31 | #decrypted_secret_content = "" 32 | signer = oci.auth.signers.get_resource_principals_signer() 33 | try: 34 | client = oci.secrets.SecretsClient({}, signer=signer) 35 | secret_content = client.get_secret_bundle(secret_ocid).data.secret_bundle_content.content.encode('utf-8') 36 | except Exception as ex: 37 | print("ERROR: failed to retrieve the secret content", ex, flush=True) 38 | raise 39 | try: 40 | with open(filepath, 'wb') as secretfile: 41 | decrypted_secret_content = base64.decodebytes(secret_content) 42 | secretfile.write(decrypted_secret_content) 43 | except Exception as ex: 44 | print("ERROR: cannot write to file " + filepath, ex, flush=True) 45 | raise 46 | secret_md5 = hashlib.md5(decrypted_secret_content).hexdigest() 47 | return {"secret md5": secret_md5} 48 | 49 | 50 | def handler(ctx, data: io.BytesIO=None): 51 | logging.getLogger().info("function start") 52 | 53 | secret_ocid = secret_type = resp = "" 54 | try: 55 | cfg = dict(ctx.Config()) 56 | secret_ocid = cfg["secret_ocid"] 57 | logging.getLogger().info("Secret ocid = " + secret_ocid) 58 | secret_type = cfg["secret_type"] 59 | logging.getLogger().info("Secret type = " + secret_type) 60 | except Exception as e: 61 | print('ERROR: Missing configuration keys, secret ocid and secret_type', e, flush=True) 62 | raise 63 | 64 | if secret_type == "text": 65 | resp = get_text_secret(secret_ocid) 66 | elif secret_type == "binary": 67 | resp = get_binary_secret_into_file(secret_ocid, "/tmp/secret") 68 | else: 69 | raise ValueError('the value of the configuration parameter "secret_type" has to be either "text" or "binary"') 70 | 71 | logging.getLogger().info("function end") 72 | return response.Response( 73 | ctx, 74 | response_data=resp, 75 | headers={"Content-Type": "application/json"} 76 | ) 77 | -------------------------------------------------------------------------------- /request-info/func.py: -------------------------------------------------------------------------------- 1 | # 2 | # oci-apigw-display-httprequest-info-python version 1.0. 3 | # 4 | # Copyright (c) 2020 Oracle, Inc. 5 | # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. 6 | # 7 | 8 | import io 9 | import json 10 | import oci 11 | import logging 12 | from urllib.parse import urlparse, parse_qs 13 | 14 | from fdk import response 15 | 16 | 17 | def handler(ctx, data: io.BytesIO=None): 18 | logging.getLogger().info("function handler start") 19 | 20 | resp = {} 21 | 22 | # retrieving the request headers 23 | headers = ctx.Headers() 24 | logging.getLogger().info("Headers: " + json.dumps(headers)) 25 | resp["Headers"] = headers 26 | 27 | # retrieving the function configuration 28 | resp["Configuration"] = dict(ctx.Config()) 29 | logging.getLogger().info("Configuration: " + json.dumps(resp["Configuration"])) 30 | 31 | # retrieving the request body, e.g. {"key1":"value"} 32 | #requestbody_bytes = data.getvalue() 33 | #if requestbody_bytes==b'': 34 | # logging.getLogger().info("No request body") 35 | # requestbody = {} 36 | #else: 37 | # requestbody = json.loads(requestbody_bytes) 38 | # logging.getLogger().info() 39 | try: 40 | requestbody_str = data.getvalue().decode('UTF-8') 41 | if requestbody_str: 42 | resp["Request body"] = json.loads(requestbody_str) 43 | else: 44 | resp["Request body"] = {} 45 | except Exception as ex: 46 | print('ERROR: The request body is not JSON', ex, flush=True) 47 | raise 48 | 49 | # retrieving the request URL, e.g. "/v1/display-uri-info" 50 | requesturl = ctx.RequestURL() 51 | logging.getLogger().info("Request URL: " + json.dumps(requesturl)) 52 | resp["Request URL"] = requesturl 53 | 54 | # retrieving query string from the request URL, e.g. {"param1":["value"]} 55 | parsed_url = urlparse(requesturl) 56 | resp["Query String"] = parse_qs(parsed_url.query) 57 | logging.getLogger().info("Query string: " + json.dumps(resp["Query String"])) 58 | 59 | # retrieving the request method, e.g. "POST", "GET"... 60 | method = ctx.Method() 61 | if method: 62 | logging.getLogger().info("Request Method: " + method) 63 | resp["Request Method"] = method 64 | else: 65 | logging.getLogger().info("No Request Method") 66 | resp["Request Method"] = None 67 | 68 | # retrieving the Application ID, e.g. "ocid1.fnapp.oc1.phx.aaaaxxxx" 69 | appid = ctx.AppID() 70 | logging.getLogger().info("AppID: " + appid) 71 | resp["AppID"] = appid 72 | 73 | # retrieving the Function ID, e.g. "ocid1.fnfunc.oc1.phx.aaaaxxxxx" 74 | fnid = ctx.FnID() 75 | logging.getLogger().info("FnID: " + fnid) 76 | resp["FnID"] = fnid 77 | 78 | # retrieving the Function call ID, e.g. "01E9FE6JBW1BT0C68ZJ003KR1Q" 79 | callid = ctx.CallID() 80 | logging.getLogger().info("CallID: " + callid) 81 | resp["CallID"] = callid 82 | 83 | # retrieving the Function format, e.g. "http-stream" 84 | fnformat = ctx.Format() 85 | logging.getLogger().info("Format: " + fnformat) 86 | resp["Format"] = fnformat 87 | 88 | # retrieving the Function deadline, e.g. "2020-05-29T05:24:46Z" 89 | deadline = ctx.Deadline() 90 | logging.getLogger().info("Deadline: " + deadline) 91 | resp["Deadline"] = deadline 92 | 93 | logging.getLogger().info("function handler end") 94 | return response.Response( 95 | ctx, 96 | response_data=json.dumps(resp), 97 | headers={"Content-Type": "application/json"} 98 | ) 99 | -------------------------------------------------------------------------------- /create-file/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | UTF-8 8 | 1.0.130 9 | 10 | com.example.fn 11 | create-file 12 | 1.0.0 13 | 14 | 15 | 16 | 17 | com.oracle.oci.sdk 18 | oci-java-sdk-bom 19 | 1.36.2 20 | pom 21 | import 22 | 23 | 24 | 25 | 26 | 27 | 28 | com.fnproject.fn 29 | api 30 | ${fdk.version} 31 | 32 | 33 | com.fnproject.fn 34 | testing-core 35 | ${fdk.version} 36 | test 37 | 38 | 39 | com.fnproject.fn 40 | testing-junit4 41 | ${fdk.version} 42 | test 43 | 44 | 45 | junit 46 | junit 47 | 4.13.2 48 | test 49 | 50 | 51 | com.oracle.oci.sdk 52 | oci-java-sdk-common 53 | 1.33.2 54 | 55 | 56 | com.oracle.oci.sdk 57 | oci-java-sdk-streaming 58 | 1.33.2 59 | 60 | 61 | com.oracle.oci.sdk 62 | oci-java-sdk-objectstorage 63 | 64 | 65 | com.sun.activation 66 | jakarta.activation 67 | 1.2.1 68 | 69 | 70 | org.slf4j 71 | slf4j-api 72 | 1.7.23 73 | 74 | 75 | org.apache.commons 76 | commons-lang3 77 | 3.4 78 | 79 | 80 | javax.activation 81 | activation 82 | 1.1.1 83 | 84 | 85 | commons-io 86 | commons-io 87 | 2.6 88 | 89 | 90 | 91 | 92 | 93 | 94 | org.apache.maven.plugins 95 | maven-compiler-plugin 96 | 3.3 97 | 98 | 11 99 | 11 100 | 101 | 102 | 103 | org.apache.maven.plugins 104 | maven-surefire-plugin 105 | 2.22.1 106 | 107 | false 108 | 109 | 110 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /API-specs/petstoreAPIv3.yaml: -------------------------------------------------------------------------------- 1 | openapi: "3.0.0" 2 | info: 3 | version: 1.0.0 4 | title: Swagger Petstore 5 | description: A sample API that uses a petstore as an example to demonstrate features in the OpenAPI 3.0 specification 6 | termsOfService: http://swagger.io/terms/ 7 | contact: 8 | name: Swagger API Team 9 | email: apiteam@swagger.io 10 | url: http://swagger.io 11 | license: 12 | name: Apache 2.0 13 | url: https://www.apache.org/licenses/LICENSE-2.0.html 14 | servers: 15 | - url: http://petstore.swagger.io/api 16 | paths: 17 | /pets: 18 | get: 19 | description: | 20 | Returns all pets from the system that the user has access to 21 | operationId: findPets 22 | parameters: 23 | - name: tags 24 | in: query 25 | description: tags to filter by 26 | required: false 27 | style: form 28 | schema: 29 | type: array 30 | items: 31 | type: string 32 | - name: limit 33 | in: query 34 | description: maximum number of results to return 35 | required: false 36 | schema: 37 | type: integer 38 | format: int32 39 | responses: 40 | '200': 41 | description: pet response 42 | content: 43 | application/json: 44 | schema: 45 | type: array 46 | items: 47 | $ref: '#/components/schemas/Pet' 48 | default: 49 | description: unexpected error 50 | content: 51 | application/json: 52 | schema: 53 | $ref: '#/components/schemas/Error' 54 | post: 55 | description: Creates a new pet in the store. Duplicates are allowed 56 | operationId: addPet 57 | requestBody: 58 | description: Pet to add to the store 59 | required: true 60 | content: 61 | application/json: 62 | schema: 63 | $ref: '#/components/schemas/NewPet' 64 | responses: 65 | '201': 66 | description: pet response 67 | content: 68 | application/json: 69 | schema: 70 | $ref: '#/components/schemas/Pet' 71 | default: 72 | description: unexpected error 73 | content: 74 | application/json: 75 | schema: 76 | $ref: '#/components/schemas/Error' 77 | /pets/{id}: 78 | get: 79 | description: Returns a user based on a single ID 80 | operationId: find pet by id 81 | parameters: 82 | - name: id 83 | in: path 84 | description: ID of pet to fetch 85 | required: true 86 | schema: 87 | type: integer 88 | format: int64 89 | responses: 90 | '200': 91 | description: pet response 92 | content: 93 | application/json: 94 | schema: 95 | $ref: '#/components/schemas/Pet' 96 | default: 97 | description: unexpected error 98 | content: 99 | application/json: 100 | schema: 101 | $ref: '#/components/schemas/Error' 102 | delete: 103 | description: deletes a single pet based on the ID supplied 104 | operationId: deletePet 105 | parameters: 106 | - name: id 107 | in: path 108 | description: ID of pet to delete 109 | required: true 110 | schema: 111 | type: integer 112 | format: int64 113 | responses: 114 | '204': 115 | description: pet deleted 116 | default: 117 | description: unexpected error 118 | content: 119 | application/json: 120 | schema: 121 | $ref: '#/components/schemas/Error' 122 | components: 123 | schemas: 124 | Pet: 125 | allOf: 126 | - $ref: '#/components/schemas/NewPet' 127 | - type: object 128 | required: 129 | - id 130 | properties: 131 | id: 132 | type: integer 133 | format: int64 134 | 135 | NewPet: 136 | type: object 137 | required: 138 | - name 139 | properties: 140 | name: 141 | type: string 142 | tag: 143 | type: string 144 | 145 | Error: 146 | type: object 147 | required: 148 | - code 149 | - message 150 | properties: 151 | code: 152 | type: integer 153 | format: int32 154 | message: 155 | type: string -------------------------------------------------------------------------------- /create-file/src/main/java/com/example/fn/WriteFileProduceMessage.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** WriteFileProduceMessage version 1.2. 3 | ** 4 | ** Copyright (c) 2022 Oracle, Inc. 5 | ** Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. 6 | */ 7 | 8 | package com.example.fn; 9 | 10 | import com.oracle.bmc.auth.ResourcePrincipalAuthenticationDetailsProvider; 11 | import com.oracle.bmc.objectstorage.ObjectStorage; 12 | import com.oracle.bmc.objectstorage.ObjectStorageClient; 13 | import com.oracle.bmc.objectstorage.requests.PutObjectRequest; 14 | import com.oracle.bmc.objectstorage.responses.PutObjectResponse; 15 | import com.oracle.bmc.auth.AuthenticationDetailsProvider; 16 | import com.oracle.bmc.auth.SimpleAuthenticationDetailsProvider; 17 | import com.oracle.bmc.streaming.StreamAdminClient; 18 | import com.oracle.bmc.streaming.StreamClient; 19 | import com.oracle.bmc.streaming.model.PutMessagesDetails; 20 | import com.oracle.bmc.streaming.model.PutMessagesDetailsEntry; 21 | import com.oracle.bmc.streaming.model.PutMessagesResult; 22 | import com.oracle.bmc.streaming.model.PutMessagesResultEntry; 23 | import com.oracle.bmc.streaming.model.StreamSummary; 24 | import com.oracle.bmc.streaming.requests.ListStreamsRequest; 25 | import com.oracle.bmc.streaming.requests.PutMessagesRequest; 26 | import com.oracle.bmc.streaming.responses.ListStreamsResponse; 27 | import java.io.FileInputStream; 28 | import java.io.FileNotFoundException; 29 | import java.io.InputStream; 30 | import java.util.Arrays; 31 | import java.util.List; 32 | import java.io.ByteArrayInputStream; 33 | import java.nio.charset.StandardCharsets; 34 | 35 | public class WriteFileProduceMessage { 36 | 37 | private ObjectStorage objStoreClient = null; 38 | 39 | final ResourcePrincipalAuthenticationDetailsProvider provider 40 | = ResourcePrincipalAuthenticationDetailsProvider.builder().build(); 41 | 42 | public WriteFileProduceMessage() { 43 | try { 44 | //print env vars in Functions container 45 | System.err.println("OCI_RESOURCE_PRINCIPAL_VERSION " + System.getenv("OCI_RESOURCE_PRINCIPAL_VERSION")); 46 | System.err.println("OCI_RESOURCE_PRINCIPAL_REGION " + System.getenv("OCI_RESOURCE_PRINCIPAL_REGION")); 47 | System.err.println("OCI_RESOURCE_PRINCIPAL_RPST " + System.getenv("OCI_RESOURCE_PRINCIPAL_RPST")); 48 | System.err.println("OCI_RESOURCE_PRINCIPAL_PRIVATE_PEM " + System.getenv("OCI_RESOURCE_PRINCIPAL_PRIVATE_PEM")); 49 | 50 | System.err.println("NAMESPACE: " + System.getenv().get("NAMESPACE")); 51 | System.err.println("STREAM_ENDPOINT: " + System.getenv().get("STREAM_ENDPOINT")); 52 | System.err.println("STREAM_OCID: " + System.getenv().get("STREAM_OCID")); 53 | 54 | objStoreClient = new ObjectStorageClient(provider); 55 | 56 | } catch (Throwable ex) { 57 | System.err.println("Failed to instantiate ObjectStorage client - " + ex.getMessage()); 58 | } 59 | 60 | } // end WriteFileProduceMessage constructor method 61 | 62 | public static class ObjectInfo { 63 | 64 | private String name; 65 | private String bucketName; 66 | private String content; 67 | public String getBucketName() { 68 | return bucketName; 69 | } 70 | public void setBucketName(String bucketName) { 71 | this.bucketName = bucketName; 72 | } 73 | public ObjectInfo() { 74 | } 75 | public String getName() { 76 | return name; 77 | } 78 | public void setName(String name) { 79 | this.name = name; 80 | } 81 | public String getContent() { 82 | return content; 83 | } 84 | public void setContent(String content) { 85 | this.content = content; 86 | } 87 | } // End ObjectInfo static class 88 | 89 | private static void publishMessage(ObjectInfo objectInfo, StreamClient streamClient, String streamId) { 90 | 91 | String result = null; 92 | 93 | PutMessagesDetails putMessagesDetails = PutMessagesDetails.builder() 94 | .messages(Arrays.asList(PutMessagesDetailsEntry.builder() 95 | .key(objectInfo.name.getBytes(StandardCharsets.UTF_8)) 96 | .value(objectInfo.content.getBytes(StandardCharsets.UTF_8)).build())) 97 | .build(); 98 | 99 | PutMessagesRequest putMessagesRequest = PutMessagesRequest.builder() 100 | .putMessagesDetails(putMessagesDetails) 101 | .streamId(streamId) 102 | .build(); 103 | 104 | PutMessagesResult putMessagesResult = streamClient.putMessages(putMessagesRequest).getPutMessagesResult(); 105 | System.err.println("pushed message"); 106 | 107 | for (PutMessagesResultEntry entry : putMessagesResult.getEntries()) { 108 | if (entry.getError() != null) { 109 | result = "Put message error " + entry.getErrorMessage(); 110 | System.err.println(result); 111 | } else { 112 | result = "Message pushed to offset " + entry.getOffset() + " in partition " + entry.getPartition(); 113 | System.err.println(result); 114 | } 115 | } 116 | 117 | } // End publishMessage method 118 | 119 | public String handle(ObjectInfo objectInfo) { 120 | 121 | String result = "FAILED"; 122 | String objResult = "Obj Failed"; 123 | String streamResult = "Stream Failed"; 124 | 125 | if (objStoreClient == null) { 126 | System.err.println("There was a problem creating the ObjectStorage Client object. Please check logs"); 127 | return result; 128 | } 129 | try { 130 | 131 | String nameSpace = System.getenv().get("NAMESPACE"); 132 | 133 | // Put a new file in the indicated Object Storage Bucket 134 | PutObjectRequest por = PutObjectRequest.builder() 135 | .namespaceName(nameSpace) 136 | .bucketName(objectInfo.bucketName) 137 | .objectName(objectInfo.name) 138 | .putObjectBody(new ByteArrayInputStream(objectInfo.content.getBytes(StandardCharsets.UTF_8))) 139 | .build(); 140 | 141 | PutObjectResponse poResp = objStoreClient.putObject(por); 142 | objResult = "Successfully put to Object Storage with Filename=" + objectInfo.name + " Bucket=" + objectInfo.bucketName; 143 | System.err.println(objResult); 144 | 145 | // Import Stream OCID & Endpoint 146 | String ociMessageEndpoint = System.getenv().get("STREAM_ENDPOINT"); 147 | String ociStreamOcid = System.getenv().get("STREAM_OCID"); 148 | 149 | // Create a stream client using the provided message endpoint. 150 | StreamClient streamClient = StreamClient.builder().endpoint(ociMessageEndpoint).build(provider); 151 | 152 | // publish data from objectInfo as a message to the stream 153 | publishMessage(objectInfo, streamClient, ociStreamOcid); 154 | 155 | // if an exception is NOT thrown, log result... 156 | streamResult = "Successfully produced message to Stream with Key=" + objectInfo.name + " Message=" + objectInfo.content; 157 | System.err.println(streamResult); 158 | 159 | } catch (Throwable e) { 160 | // ...otherwise, create an error message 161 | System.err.println("Error storing object in bucket or writing to stream " + e.getMessage()); 162 | result = "Error storing object in bucket or writing to stream " + e.getMessage(); 163 | } 164 | 165 | return streamResult + "\n" + objResult; 166 | 167 | } // End Fn Function handle method 168 | 169 | } // End WriteFileProduceMessage class 170 | --------------------------------------------------------------------------------