Esempio n. 1
0
    def test_stateless(self):
        functions = StatefulFunctions()

        @functions.bind(typename="org.foo/greeter")
        def greeter(context, message):
            pass

        fun = functions.for_typename("org.foo/greeter")
        self.assertListEqual(fun.storage_spec.specs, [])
Esempio n. 2
0
    def test_async(self):
        functions = StatefulFunctions()

        @functions.bind(typename="org.foo/greeter",
                        specs=[ValueSpec(name='seen_count', type=IntType)])
        async def greeter(context, message):
            pass

        fun = functions.for_typename("org.foo/greeter")
        self.assertTrue(fun.is_async)
        self.assertIsNotNone(fun.storage_spec)
Esempio n. 3
0
    def test_state_spec(self):
        functions = StatefulFunctions()

        foo = ValueSpec(name='foo', type=IntType)
        bar = ValueSpec(name='bar', type=StringType)

        @functions.bind(typename="org.foo/greeter", specs=[foo, bar])
        def greeter(context, message):
            pass

        fun = functions.for_typename("org.foo/greeter")
        self.assertListEqual(fun.storage_spec.specs, [foo, bar])
    def test_integration_access_non_registered_state(self):
        functions = StatefulFunctions()

        @functions.bind(
            typename='org.foo/bar',
            states=[StateSpec('seen')])
        async def fun(context, message):
            ignored = context['non_registered_state']

        #
        # build the invocation
        #
        builder = InvocationBuilder()
        builder.with_target("org.foo", "bar", "0")

        seen = SeenCount()
        seen.seen = 100
        builder.with_state("seen", seen)

        builder.with_invocation(Any(), None)

        #
        # assert error is raised on invoke
        #
        with self.assertRaises(StateRegistrationError):
            async_round_trip(functions, builder)
    def test_integration(self):
        functions = StatefulFunctions()

        @functions.bind('org.foo/greeter')
        async def fun(context, message):
            any = Any()
            any.type_url = 'type.googleapis.com/k8s.demo.SeenCount'
            context.send("bar.baz/foo", "12345", any)

        #
        # build the invocation
        #
        builder = InvocationBuilder()
        builder.with_target("org.foo", "greeter", "0")

        arg = LoginEvent()
        arg.user_name = "user-1"
        builder.with_invocation(arg, ("org.foo", "greeter-java", "0"))

        #
        # invoke
        #
        result_json = async_round_trip(functions, builder)

        # assert outgoing message
        second_out_message = json_at(result_json, NTH_OUTGOING_MESSAGE(0))
        self.assertEqual(second_out_message['target']['namespace'], 'bar.baz')
        self.assertEqual(second_out_message['target']['type'], 'foo')
        self.assertEqual(second_out_message['target']['id'], '12345')
        self.assertEqual(second_out_message['argument']['typename'], 'type.googleapis.com/k8s.demo.SeenCount')
Esempio n. 6
0
    def test_wrong_signature(self):
        functions = StatefulFunctions()

        with self.assertRaises(ValueError):

            @functions.bind(typename="org.foo/greeter",
                            specs=[ValueSpec(name="bar", type=IntType)])
            def foo(message):  # missing context
                pass
Esempio n. 7
0
    def test_duplicate_state(self):
        functions = StatefulFunctions()

        with self.assertRaises(ValueError):

            @functions.bind(typename="org.foo/greeter",
                            specs=[
                                ValueSpec(name="bar", type=IntType),
                                ValueSpec(name="bar", type=IntType)
                            ])
            def foo(context, message):
                pass
    def test_integration_incomplete_context(self):
        functions = StatefulFunctions()

        @functions.bind(
            typename='org.foo/bar',
            states=[
                StateSpec('seen'),
                StateSpec('missing_state_1'),
                StateSpec('missing_state_2', expire_after=AfterWrite(timedelta(milliseconds=2000)))
            ])
        async def fun(context, message):
            pass

        #
        # build an invocation that provides only 'seen' state
        #
        builder = InvocationBuilder()
        builder.with_target("org.foo", "bar", "0")

        seen = SeenCount()
        seen.seen = 100
        builder.with_state("seen", seen)

        builder.with_invocation(Any(), None)

        #
        # invoke
        #
        result_json = async_round_trip(functions, builder)

        #
        # assert indicated missing states
        #
        missing_state_1_spec = json_at(result_json, NTH_MISSING_STATE_SPEC(0))
        self.assertEqual(missing_state_1_spec['state_name'], 'missing_state_1')

        missing_state_2_spec = json_at(result_json, NTH_MISSING_STATE_SPEC(1))
        self.assertEqual(missing_state_2_spec['state_name'], 'missing_state_2')
        self.assertEqual(missing_state_2_spec['expiration_spec']['mode'], 'AFTER_WRITE')
        self.assertEqual(missing_state_2_spec['expiration_spec']['expire_after_millis'], '2000')
Esempio n. 9
0
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
# limitations under the License.
################################################################################

from remote_module_verification_pb2 import Invoke, InvokeResult, InvokeCount

from statefun import StatefulFunctions
from statefun import StateSpec
from statefun import RequestReplyHandler
from statefun import kafka_egress_record

import uuid

functions = StatefulFunctions()


@functions.bind(typename="org.apache.flink.statefun.e2e.remote/counter",
                states=[StateSpec('invoke_count')])
def counter(context, invoke: Invoke):
    """
    Keeps count of the number of invocations, and forwards that count
    to be sent to the Kafka egress. We do the extra forwarding instead
    of directly sending to Kafka, so that we cover inter-function
    messaging in our E2E test.
    """
    invoke_count = context.state('invoke_count').unpack(InvokeCount)
    if not invoke_count:
        invoke_count = InvokeCount()
        invoke_count.count = 1
    def test_integration(self):
        functions = StatefulFunctions()

        @functions.bind(
            typename='org.foo/greeter',
            states=[StateSpec('seen')])
        def fun(context, message):
            # state access
            seen = context.state('seen').unpack(SeenCount)
            seen.seen += 1
            context.state('seen').pack(seen)

            # regular state access
            seenAny = context['seen']
            seenAny.Unpack(seen)

            # sending and replying
            context.pack_and_reply(seen)

            any = Any()
            any.type_url = 'type.googleapis.com/k8s.demo.SeenCount'
            context.send("bar.baz/foo", "12345", any)

            # delayed messages
            context.send_after(timedelta(hours=1), "night/owl", "1", any)

            # egresses
            context.send_egress("foo.bar.baz/my-egress", any)
            context.pack_and_send_egress("foo.bar.baz/my-egress", seen)

            # kafka egress
            context.pack_and_send_egress("sdk/kafka",
                                         kafka_egress_record(topic="hello", key=u"hello world", value=seen))
            context.pack_and_send_egress("sdk/kafka",
                                         kafka_egress_record(topic="hello", value=seen))

            # AWS Kinesis generic egress
            context.pack_and_send_egress("sdk/kinesis",
                                         kinesis_egress_record(
                                             stream="hello",
                                             partition_key=u"hello world",
                                             value=seen,
                                             explicit_hash_key=u"1234"))
            context.pack_and_send_egress("sdk/kinesis",
                                         kinesis_egress_record(
                                             stream="hello",
                                             partition_key=u"hello world",
                                             value=seen))

        #
        # build the invocation
        #
        builder = InvocationBuilder()
        builder.with_target("org.foo", "greeter", "0")

        seen = SeenCount()
        seen.seen = 100
        builder.with_state("seen", seen)

        arg = LoginEvent()
        arg.user_name = "user-1"
        builder.with_invocation(arg, ("org.foo", "greeter-java", "0"))

        #
        # invoke
        #
        result_json = round_trip(functions, builder)

        # assert first outgoing message
        first_out_message = json_at(result_json, NTH_OUTGOING_MESSAGE(0))
        self.assertEqual(first_out_message['target']['namespace'], 'org.foo')
        self.assertEqual(first_out_message['target']['type'], 'greeter-java')
        self.assertEqual(first_out_message['target']['id'], '0')
        self.assertEqual(first_out_message['argument']['typename'], 'type.googleapis.com/k8s.demo.SeenCount')

        # assert second outgoing message
        second_out_message = json_at(result_json, NTH_OUTGOING_MESSAGE(1))
        self.assertEqual(second_out_message['target']['namespace'], 'bar.baz')
        self.assertEqual(second_out_message['target']['type'], 'foo')
        self.assertEqual(second_out_message['target']['id'], '12345')
        self.assertEqual(second_out_message['argument']['typename'], 'type.googleapis.com/k8s.demo.SeenCount')

        # assert state mutations
        first_mutation = json_at(result_json, NTH_STATE_MUTATION(0))
        self.assertEqual(first_mutation['mutation_type'], 'MODIFY')
        self.assertEqual(first_mutation['state_name'], 'seen')
        self.assertIsNotNone(first_mutation['state_value'])

        # assert delayed
        first_delayed = json_at(result_json, NTH_DELAYED_MESSAGE(0))
        self.assertEqual(int(first_delayed['delay_in_ms']), 1000 * 60 * 60)

        # assert egresses
        first_egress = json_at(result_json, NTH_EGRESS(0))
        self.assertEqual(first_egress['egress_namespace'], 'foo.bar.baz')
        self.assertEqual(first_egress['egress_type'], 'my-egress')
        self.assertEqual(first_egress['argument']['typename'], 'type.googleapis.com/k8s.demo.SeenCount')