diff --git a/Procfile b/Procfile new file mode 100644 index 0000000..bb57f5a --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +web: python -m parliament . diff --git a/README.md b/README.md index 19e8258..5137e24 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,29 @@ -# Go HTTP Function +# Python HTTP Function -Welcome to your new Go Function! The boilerplate function code can be found in -[`handle.go`](handle.go). This Function responds to HTTP requests. +Welcome to your new Python function project! The boilerplate function +code can be found in [`func.py`](./func.py). This function will respond +to incoming HTTP GET and POST requests. -## Development +## Endpoints -Develop new features by adding a test to [`handle_test.go`](handle_test.go) for -each feature, and confirm it works with `go test`. +Running this function will expose three endpoints. -Update the running analog of the function using the `func` CLI or client -library, and it can be invoked from your browser or from the command line: + * `/` The endpoint for your function. + * `/health/readiness` The endpoint for a readiness health check + * `/health/liveness` The endpoint for a liveness health check + +The health checks can be accessed in your browser at +[http://localhost:8080/health/readiness]() and +[http://localhost:8080/health/liveness](). + +You can use `func invoke` to send an HTTP request to the function endpoint. + + +## Testing + +This function project includes a [unit test](./test_func.py). Update this +as you add business logic to your function in order to test its behavior. ```console -curl http://myfunction.example.com/ +python test_func.py ``` - -For more, see [the complete documentation]('https://github.com/knative/func/tree/main/docs') - - diff --git a/app.sh b/app.sh new file mode 100755 index 0000000..4da37d4 --- /dev/null +++ b/app.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +exec python -m parliament "$(dirname "$0")" diff --git a/func.py b/func.py new file mode 100644 index 0000000..444684f --- /dev/null +++ b/func.py @@ -0,0 +1,63 @@ +from parliament import Context +from flask import Request +import json + + +# parse request body, json data or URL query parameters +def payload_print(req: Request) -> str: + if req.method == "POST": + if req.is_json: + return json.dumps(req.json) + "\n" + else: + # MultiDict needs some iteration + ret = "{" + + for key in req.form.keys(): + ret += '"' + key + '": "'+ req.form[key] + '", ' + + return ret[:-2] + "}\n" if len(ret) > 2 else "{}" + + elif req.method == "GET": + # MultiDict needs some iteration + ret = "{" + + for key in req.args.keys(): + ret += '"' + key + '": "' + req.args[key] + '", ' + + return ret[:-2] + "}\n" if len(ret) > 2 else "{}" + + +# pretty print the request to stdout instantaneously +def pretty_print(req: Request) -> str: + ret = str(req.method) + ' ' + str(req.url) + ' ' + str(req.host) + '\n' + for (header, values) in req.headers: + ret += " " + str(header) + ": " + values + '\n' + + if req.method == "POST": + ret += "Request body:\n" + ret += " " + payload_print(req) + '\n' + + elif req.method == "GET": + ret += "URL Query String:\n" + ret += " " + payload_print(req) + '\n' + + return ret + + +def main(context: Context): + """ + Function template + The context parameter contains the Flask request object and any + CloudEvent received with the request. + """ + + # Add your business logic here + print("Received request") + + if 'request' in context.keys(): + ret = pretty_print(context.request) + print(ret, flush=True) + return payload_print(context.request), 200 + else: + print("Empty request", flush=True) + return "{}", 200 diff --git a/func.yaml b/func.yaml index e1782a2..4d4f252 100644 --- a/func.yaml +++ b/func.yaml @@ -1,7 +1,7 @@ specVersion: 0.35.0 -name: hello -runtime: go -created: 2023-12-07T19:54:11.056629+01:00 +name: hello2 +runtime: python +created: 2023-12-08T16:20:58.104476+01:00 build: git: url: https://git.le-maire.eu/alemaire/knative-function.git # required, git repository with the function source code diff --git a/go.mod b/go.mod deleted file mode 100644 index 4b9f90e..0000000 --- a/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module function - -go 1.14 diff --git a/handle.go b/handle.go deleted file mode 100644 index 9883591..0000000 --- a/handle.go +++ /dev/null @@ -1,41 +0,0 @@ -package function - -import ( - "context" - "fmt" - "net/http" - "strings" -) - -// Handle an HTTP Request. -func Handle(ctx context.Context, res http.ResponseWriter, req *http.Request) { - /* - * YOUR CODE HERE - * - * Try running `go test`. Add more test as you code in `handle_test.go`. - */ - - fmt.Println("Received request") - fmt.Println(prettyPrint(req)) // echo to local output - fmt.Fprintf(res, prettyPrint(req)) // echo to caller -} - -func prettyPrint(req *http.Request) string { - b := &strings.Builder{} - fmt.Fprintf(b, "%v %v %v %v\n", req.Method, req.URL, req.Proto, req.Host) - for k, vv := range req.Header { - for _, v := range vv { - fmt.Fprintf(b, " %v: %v\n", k, v) - } - } - - if req.Method == "POST" { - req.ParseForm() - fmt.Fprintln(b, "Body:") - for k, v := range req.Form { - fmt.Fprintf(b, " %v: %v\n", k, v) - } - } - - return b.String() -} diff --git a/handle_test.go b/handle_test.go deleted file mode 100644 index a98b4d8..0000000 --- a/handle_test.go +++ /dev/null @@ -1,26 +0,0 @@ -package function - -import ( - "context" - "net/http" - "net/http/httptest" - "testing" -) - -// TestHandle ensures that Handle executes without error and returns the -// HTTP 200 status code indicating no errors. -func TestHandle(t *testing.T) { - var ( - w = httptest.NewRecorder() - req = httptest.NewRequest("GET", "http://example.com/test", nil) - res *http.Response - ) - - Handle(context.Background(), w, req) - res = w.Result() - defer res.Body.Close() - - if res.StatusCode != 200 { - t.Fatalf("unexpected response code: %v", res.StatusCode) - } -} diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..0229b30 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +parliament-functions==0.1.0 diff --git a/test_func.py b/test_func.py new file mode 100644 index 0000000..ac5977d --- /dev/null +++ b/test_func.py @@ -0,0 +1,13 @@ +import unittest + +func = __import__("func") + +class TestFunc(unittest.TestCase): + + def test_func_empty_request(self): + resp, code = func.main({}) + self.assertEqual(resp, "{}") + self.assertEqual(code, 200) + +if __name__ == "__main__": + unittest.main() \ No newline at end of file