diff --git a/examples/complex_inputs/README.md b/examples/complex_inputs/README.md new file mode 100644 index 0000000..9b9bb6b --- /dev/null +++ b/examples/complex_inputs/README.md @@ -0,0 +1,11 @@ +# Complex Inputs with Multiple Files + +In this example, we see how to author a Foundry Function authored in Python which can accept a JSON payload and a +collection of files as input. +The files are provided in two ways. The first is as an array and the second is as an individual field. +The files are read in full and then concatenated and returned. +JSON schemas are provided which show how to integrate successfully with Fusion. + +### Fusion Workflow + + diff --git a/examples/complex_inputs/complex_inputs_example.py b/examples/complex_inputs/complex_inputs_example.py deleted file mode 100644 index ec1e007..0000000 --- a/examples/complex_inputs/complex_inputs_example.py +++ /dev/null @@ -1,33 +0,0 @@ -"""Example Foundry Function with POST handler.""" -from crowdstrike.foundry.function import Function, Request, Response - -func = Function.instance() - - -@func.handler(method='POST', path='/my-endpoint') -def handle_complex_inputs(request: Request) -> Response: - """Implement example function handler to showcase how to handle complex inputs. - - This example demonstrates how to provide multiple inputs to a function, some of which happen to be files. - In this case, it simply echoes back the contents of those files concatenated together with spaces. - This could easily be changed to something more advanced to work with arbitrary binary. - - :param request: :class:`Request` to handle. - :return: :class:`Response` - """ - greeting = f'Welcome {request.body.get("name", "")}, age {request.body.get("age", "")}' - file_contents = [] - for v in request.files.values(): - file_contents.append(v.decode('utf-8').strip()) - - return Response( - body={ - 'allText': ' '.join(file_contents), - 'greeting': greeting, - }, - code=200, - ) - - -if __name__ == '__main__': - func.run() diff --git a/examples/complex_inputs/lorem-ipsum-1.txt b/examples/complex_inputs/functions/complex_input_tester/lorem-ipsum-1.txt similarity index 100% rename from examples/complex_inputs/lorem-ipsum-1.txt rename to examples/complex_inputs/functions/complex_input_tester/lorem-ipsum-1.txt diff --git a/examples/complex_inputs/lorem-ipsum-2.txt b/examples/complex_inputs/functions/complex_input_tester/lorem-ipsum-2.txt similarity index 100% rename from examples/complex_inputs/lorem-ipsum-2.txt rename to examples/complex_inputs/functions/complex_input_tester/lorem-ipsum-2.txt diff --git a/examples/complex_inputs/lorem-ipsum-3.txt b/examples/complex_inputs/functions/complex_input_tester/lorem-ipsum-3.txt similarity index 100% rename from examples/complex_inputs/lorem-ipsum-3.txt rename to examples/complex_inputs/functions/complex_input_tester/lorem-ipsum-3.txt diff --git a/examples/complex_inputs/functions/complex_input_tester/main.py b/examples/complex_inputs/functions/complex_input_tester/main.py new file mode 100644 index 0000000..2e3c574 --- /dev/null +++ b/examples/complex_inputs/functions/complex_input_tester/main.py @@ -0,0 +1,35 @@ +from crowdstrike.foundry.function import Function, Request, Response, APIError +from logging import Logger + +func = Function.instance() + + +@func.handler(method='POST', path='/complex-test') +def complex_handler(request: Request, config: [dict[str, any], None], logger: Logger) -> Response: + """ + Implement example function handler to showcase how to handle complex inputs. + + This example demonstrates how to provide multiple inputs to a function, some of which happen to be files. + In this case, it simply echoes back the contents of those files concatenated together with spaces. + This could easily be changed to something more advanced to work with arbitrary binary. + + :param request: :class:`Request` to handle. + :return: :class:`Response` + """ + greeting = f'Welcome {request.body.get("name", "")}, age {request.body.get("age", "")}' + file_contents = [] + for v in request.files.values(): + file_contents.append(v.decode('utf-8').strip()) + + all_text = ' '.join(file_contents) + return Response( + body={ + 'allText': all_text[0:max(len(all_text), 10000)], # returns up to the first 10,000 characters + 'greeting': greeting, + }, + code=200, + ) + + +if __name__ == '__main__': + func.run() diff --git a/examples/complex_inputs/functions/complex_input_tester/request_schema.json b/examples/complex_inputs/functions/complex_input_tester/request_schema.json new file mode 100644 index 0000000..afcd59e --- /dev/null +++ b/examples/complex_inputs/functions/complex_input_tester/request_schema.json @@ -0,0 +1,33 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "files": { + "type": "array", + "title": "Files", + "description": "LogScale files to process. Expected to be JSON.", + "items": { + "type": "string", + "format": "downloadableFile" + } + }, + "file2": { + "title": "Other File", + "description": "Some other file field.", + "type": "string", + "format": "downloadableFile" + }, + "name": { + "type": "string", + "title": "Name", + "description": "First name of the user." + }, + "age": { + "type": "integer", + "title": "Age", + "description": "Age of the user." + } + }, + "required": [ + ] +} \ No newline at end of file diff --git a/examples/complex_inputs/functions/complex_input_tester/requirements.txt b/examples/complex_inputs/functions/complex_input_tester/requirements.txt new file mode 100644 index 0000000..78be074 --- /dev/null +++ b/examples/complex_inputs/functions/complex_input_tester/requirements.txt @@ -0,0 +1 @@ +crowdstrike-foundry-function==1.1.3 diff --git a/examples/complex_inputs/functions/complex_input_tester/response_schema.json b/examples/complex_inputs/functions/complex_input_tester/response_schema.json new file mode 100644 index 0000000..a582d0e --- /dev/null +++ b/examples/complex_inputs/functions/complex_input_tester/response_schema.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "allText": { + "type": "string", + "title": "File Contents" + }, + "greeting": { + "type": "string", + "title": "Greeting" + } + }, + "required": [ + ] +} \ No newline at end of file diff --git a/examples/complex_inputs/test.sh b/examples/complex_inputs/functions/complex_input_tester/test.sh similarity index 92% rename from examples/complex_inputs/test.sh rename to examples/complex_inputs/functions/complex_input_tester/test.sh index 6dddafd..ba7a3e6 100755 --- a/examples/complex_inputs/test.sh +++ b/examples/complex_inputs/functions/complex_input_tester/test.sh @@ -1,7 +1,7 @@ curl -X POST --location "http://localhost:8081" \ -H "Content-Type: multipart/form-data" \ -F meta='{ - "url": "/my-endpoint", + "url": "/complex-test", "method": "POST" }' \ -F body='{ diff --git a/examples/complex_inputs/fusion_screenshot.png b/examples/complex_inputs/fusion_screenshot.png new file mode 100644 index 0000000..4e7a5c9 Binary files /dev/null and b/examples/complex_inputs/fusion_screenshot.png differ diff --git a/examples/complex_inputs/manifest.yml b/examples/complex_inputs/manifest.yml new file mode 100644 index 0000000..5083d8d --- /dev/null +++ b/examples/complex_inputs/manifest.yml @@ -0,0 +1,53 @@ +name: complex-fn-python-example +description: "" +logo: "" +vendor: "" +vendor_products: [] +use_case: Cloud security +manifest_version: "2023-05-09" +ignored: + - .+/node_modules$ + - .+/node_modules/.+ + - .+/venv$ + - .+/venv/.+ +ui: + homepage: "" + extensions: [] + pages: {} + dashboards: {} + navigation: {} +api_integrations: [] +rtr_scripts: [] +collections: [] +auth: + scopes: [] + permissions: {} + roles: [] +functions: + - name: complex_input_tester + config: null + description: Exercises reading in a variety of input types, including an array of files. + path: functions/complex_input_tester + environment_variables: {} + handlers: + - name: handle_complex_inputs + description: Exercises reading in a variety of input types, including an array of files. + method: POST + api_path: /complex-test + payload_type: complex + request_schema: request_schema.json + response_schema: response_schema.json + workflow_integration: + disruptive: false + system_action: false + tags: + - complex-fn-python-example + - Utility + permissions: [] + language: python +workflows: [] +parsers: [] +logscale: + saved_searches: [] +lookup_files: [] +docs: {}