1
1
import argparse
2
2
import logging
3
- import json
4
3
from func_python .cloudevent import serve
5
4
from cloudevents .http import CloudEvent
6
5
7
6
# Set the default logging level to INFO
8
7
logging .basicConfig (level = logging .INFO )
9
8
10
- # Parse command line arguments
11
- parser = argparse .ArgumentParser (description = 'Serve a CloudEvents Function' )
9
+ # Allow this test to be either instanced (default) or --static
10
+ # to test the two different primary method signatures supported in the
11
+ # final Function.
12
+ parser = argparse .ArgumentParser (description = 'Serve a Test CloudEvent Function' )
12
13
parser .add_argument ('--static' , action = 'store_true' ,
13
- help = 'Serve the static handler (default is instanced)' )
14
+ help = 'Serve the example static handler (default is to '
15
+ 'instantiate and serve the example class)' )
14
16
args = parser .parse_args ()
15
17
16
18
17
- # Static handler implementation
19
+ # Example static handler.
18
20
# Enable with --static
19
21
# Must be named exactly "handle"
20
22
async def handle (scope , receive , send ):
21
- """Static handler for CloudEvents"""
22
- logging .info ("CloudEvent static handler called" )
23
+ """ handle is an example of a static handler which can be sent to the
24
+ middleware as a function. It will be wrapped in a default Function
25
+ instance before being served as an ASGI application.
26
+ """
27
+ logging .info ("Static CloudEvent handler invoked" )
23
28
24
- # Process the CloudEvent from the scope
25
- event = scope . get ( "event" )
29
+ # Access the CloudEvent from the scope
30
+ event = scope [ "event" ]
26
31
if not event :
27
32
error_event = CloudEvent (
28
- {"type" : "dev.functions.error" , "source" : "/cloudevent /error" },
29
- {"message" : "No CloudEvent found in request " }
33
+ {"type" : "dev.functions.error" , "source" : "/fcloudevent /error" },
34
+ {"message" : "No CloudEvent found in scope " }
30
35
)
31
- await send (error_event , 400 )
36
+ await send (error_event , 500 )
32
37
return
33
38
34
- # Log the received event
35
- logging .info (f"Received event type: { event ['type' ]} " )
36
- logging .info (f"Received event source: { event ['source' ]} " )
37
- logging .info (f"Received event data: { json .dumps (event .data )} " )
39
+ logging .info (f"Received CloudEvent: type={ event ['type' ]} , source={ event ['source' ]} " )
40
+
41
+ # Handle event data - it might be bytes or dict
42
+ event_data = event .data
43
+ if isinstance (event_data , bytes ):
44
+ import json
45
+ event_data = json .loads (event_data )
46
+ logging .info (f"CloudEvent data: { event_data } " )
38
47
39
- # Create and send response CloudEvent
40
48
response_event = CloudEvent (
41
- {"type" : "dev.functions.response" , "source" : "/cloudevent/processor" },
42
- {"message" : "Processed static" , "original_data" : event .data }
49
+ {
50
+ "type" : "com.example.response.static" ,
51
+ "source" : "/fcloudevent/static" ,
52
+ },
53
+ {
54
+ "message" : "OK: static CloudEvent handler" ,
55
+ "received_event_type" : event ['type' ],
56
+ "received_event_source" : event ['source' ],
57
+ "received_data" : event_data
58
+ }
43
59
)
60
+
44
61
await send (response_event )
45
62
46
63
47
- # Instanced handler implementation
64
+ # Example instanced handler
65
+ # This is the default expected by this test.
66
+ # The class can be named anything, but there must be a constructor named "new"
67
+ # which returns an object with an async method "handle" conforming to the ASGI
68
+ # callable's method signature.
48
69
class MyCloudEventFunction :
49
70
def __init__ (self ):
50
71
self .event_count = 0
51
72
logging .info ("CloudEvent function instance created" )
52
73
53
74
async def handle (self , scope , receive , send ):
54
- """Handle CloudEvents in an instanced function"""
55
- event = scope .get ("event" )
75
+ logging .info ("Instanced CloudEvent handler invoked" )
76
+
77
+ # Access the CloudEvent from the scope
78
+ event = scope ["event" ]
56
79
if not event :
80
+ # This shouldn't happen with our fix, but let's handle it gracefully
57
81
error_event = CloudEvent (
58
- {"type" : "dev.functions.error" , "source" : "/cloudevent /error" },
59
- {"message" : "No CloudEvent found in request " }
82
+ {"type" : "dev.functions.error" , "source" : "/fcloudevent /error" },
83
+ {"message" : "No CloudEvent found in scope " }
60
84
)
61
- await send (error_event , 400 )
85
+ await send (error_event , 500 )
62
86
return
63
87
64
88
self .event_count += 1
65
89
66
- # Log the received event
67
- logging .info (f"Received event type: { event ['type' ]} " )
68
- logging .info (f"Received event source: { event ['source' ]} " )
69
- logging .info (f"Received event data: { json .dumps (event .data )} " )
90
+ logging .info (f"Received CloudEvent #{ self .event_count } : type={ event ['type' ]} , source={ event ['source' ]} " )
91
+
92
+ # Handle event data - it might be bytes or dict
93
+ event_data = event .data
94
+ if isinstance (event_data , bytes ):
95
+ import json
96
+ event_data = json .loads (event_data )
97
+ logging .info (f"CloudEvent data: { event_data } " )
70
98
logging .info (f"Total events processed: { self .event_count } " )
71
99
72
- # Create and send response CloudEvent
73
100
response_event = CloudEvent (
74
- {"type" : "dev.functions.response" , "source" : "/cloudevent/processor" },
75
101
{
76
- "message" : "Processed instanced" ,
77
- "original_data" : event .data ,
78
- "count" : self .event_count
102
+ "type" : "com.example.response.instanced" ,
103
+ "source" : "/fcloudevent/instanced" ,
104
+ },
105
+ {
106
+ "message" : "OK: instanced CloudEvent handler" ,
107
+ "event_count" : self .event_count ,
108
+ "received_event_type" : event ['type' ],
109
+ "received_event_source" : event ['source' ],
110
+ "received_data" : event_data
79
111
}
80
112
)
81
- await send (response_event )
113
+
114
+ # Demonstrate different sending methods
115
+ if self .event_count % 2 == 0 :
116
+ # Use structured encoding (default)
117
+ logging .info ("Sending structured CloudEvent response" )
118
+ await send .structured (response_event )
119
+ else :
120
+ # Use binary encoding
121
+ logging .info ("Sending binary CloudEvent response" )
122
+ await send .binary (response_event )
82
123
83
124
def alive (self ):
84
125
logging .info ("Liveness checked" )
@@ -93,16 +134,26 @@ def stop(self):
93
134
94
135
95
136
# Function instance constructor
137
+ # expected to be named exactly "new"
138
+ # Must return a object which exposes a method "handle" which conforms to the
139
+ # ASGI specification's method signature.
96
140
def new ():
97
- """Create a new function instance"""
141
+ """ new is the factory function (or constructor) which will create
142
+ a new function instance when invoked. This must be named "new", and the
143
+ structure returned must include a method named "handle" which implements
144
+ the ASGI specification's method signature. The name of the class itself
145
+ can be changed.
146
+ """
98
147
return MyCloudEventFunction ()
99
148
100
149
101
- # Run the example
150
+ # Run the example.
151
+ # Start either the static or instanced handler depending on flag --static
102
152
if __name__ == "__main__" :
103
153
if args .static :
104
154
logging .info ("Starting static CloudEvent handler" )
105
155
serve (handle )
106
156
else :
107
157
logging .info ("Starting instanced CloudEvent handler" )
108
158
serve (new )
159
+
0 commit comments