Exemplo n.º 1
0
async def consume_forever(consumer: AIOKafkaConsumer):
    """ Infinite loop reading the messages
    sent from the flink cluster back to the application"""

    # Iterate through the messages and change the
    # future of the dict to be this result
    logger.info('Consumer starting to consumer messages')
    async for msg in consumer:
        # if the message is for our worker, get it
        if msg.key.decode('utf-8') == WORKER_ID:
            # logger.info(f'Received message! {msg.value}')

            resp = ResponseMessage()
            resp.ParseFromString(msg.value)

            # set the result of the future in the dict
            if resp.request_id in messages:
                # logger.info(f'Setting future for message {resp.request_id}')

                if messages[resp.request_id].done():
                    logger.warning(
                        f'Future was already done while setting it {resp.request_id}'
                    )
                else:
                    messages[resp.request_id].set_result(resp.result)

            else:
                logger.error(
                    f'Received response for an unknown message with request id: {resp.request_id}'
                )
Exemplo n.º 2
0
def remove_order(context, msg):
    state = context.state('order').unpack(Order)
    response = ResponseMessage()
    if not state:
        response.result = json.dumps({
            'result': 'failure',
            'message': 'Order does not exist.'
        })
    else:
        #logger.info(f"Deleting the order with id: {msg.remove_order.id}")
        item_to_count = {}
        items = state.items
        for i in range(len(items)):
            id = items[i].item_id
            if id not in item_to_count.keys():
                item_to_count[id] = 1
            else:
                item_to_count[id] = item_to_count[id] + 1

        for id, cnt in item_to_count.items():
            add_stock_request = OrderAddItemStockRequest()
            add_stock_request.request_info.worker_id = msg.request_info.worker_id
            add_stock_request.request_info.request_id = msg.request_info.request_id
            add_stock_request.id = id
            add_stock_request.amount = cnt

            # logger.info('Sending request to add stock back.')
            context.pack_and_send("stock/stock", str(id), add_stock_request)

        # logger.info('Deleting order.')
        del context['order']
        response.result = json.dumps({'result': 'success'})

    return response
Exemplo n.º 3
0
def create_order_with_id(context, msg):
    state = Order()
    state.id = msg.id
    state.user_id = msg.user_id
    state.paid = False
    state.total_cost = 0

    context.state('order').pack(state)
    #logger.debug(f'Created new order with id {msg.id}')

    response = ResponseMessage()
    response.result = json.dumps({'result': 'success', 'order_id': state.id})
    return response
Exemplo n.º 4
0
def remove_item(context, msg):
    orderId = msg.remove_item.id
    state = context.state('order').unpack(Order)
    response = ResponseMessage()
    if not state:
        response.result = json.dumps({
            'result': 'failure',
            'message': 'Order does not exist.'
        })
    else:
        items = state.items
        item_to_delete = msg.remove_item.itemId
        item_index = -1
        for i in range(len(items)):
            #logger.info(f"{items[i].item_id}")
            if items[i].item_id == item_to_delete:
                item_index = i
        if item_index != -1:
            state.total_cost -= items[item_index].price
            del state.items[item_index]
            #logger.info(f"Removing item {item_to_delete} from order {orderId}")
            response.result = json.dumps({'result': 'success'})

            add_stock_request = StockRequest()
            add_stock_request.request_info.worker_id = msg.request_info.worker_id
            add_stock_request.request_info.request_id = msg.request_info.request_id
            add_stock_request.add_stock.id = item_to_delete
            add_stock_request.add_stock.amount = 1

            context.pack_and_send("stock/stock", str(item_to_delete),
                                  add_stock_request)

        else:
            #logger.warning(f"Order {orderId} does not contain item with id {item_to_delete}")
            response.result = json.dumps({
                'result':
                'failure',
                'message':
                'Order does not contain requested item.'
            })

        context.state('order').pack(state)

    return response
Exemplo n.º 5
0
def order_payment_confirm(context, msg):
    response = ResponseMessage()
    if msg.actually_paid:
        state = context.state('order').unpack(Order)
        state.paid = True
        context.state('order').pack(state)

        #logger.debug("Checkout succeeded.")
        response.result = json.dumps({
            'result': 'success',
            'message': 'Checkout succeeded.'
        })
    else:
        #logger.debug("Payment cancelling failed.")
        response.result = json.dumps({
            'result': 'failure',
            'message': 'Payment cancelling failed.'
        })

    return response
Exemplo n.º 6
0
def order_checkout(context, msg):
    state = context.state('order').unpack(Order)
    if not state:
        response = ResponseMessage()
        response.result = json.dumps({
            'result': 'failure',
            'message': 'Order does not exist.'
        })
        return response

    if state.paid:
        response = ResponseMessage()
        response.result = json.dumps({
            'result': 'failure',
            'message': 'Order already paid.'
        })

        return response

    request = Order()
    request.id = msg.order_checkout.id
    request.request_info.worker_id = msg.request_info.worker_id
    request.request_info.request_id = msg.request_info.request_id
    request.user_id = state.user_id
    # to assign a repeated field we need to call extend
    request.items.extend(state.items)
    request.total_cost = state.total_cost
    request.paid = state.paid
    request.intent = Order.Intent.PAY

    #logger.debug(f'Sending order check to payments {request}')
    # send to payment service
    context.pack_and_send("payments/pay", str(request.id), request)
Exemplo n.º 7
0
def order_add_item_reply(context, msg):
    state = context.state('order').unpack(Order)
    response = ResponseMessage()
    if msg.result == 'success':
        #logger.info("Successfully added item to order.")
        new_item = Item()
        new_item.item_id = msg.item_id
        new_item.price = msg.price
        state.items.append(new_item)
        state.total_cost += msg.price
        #logger.info(f"{state}")
        context.state('order').pack(state)
        response.result = json.dumps({'result': 'success'})
    else:
        #logger.debug("No items left in stock.")
        response.result = json.dumps({
            'result': 'failure',
            'message': 'No items left in stock.'
        })
        context.state('order').pack(state)

    return response
Exemplo n.º 8
0
def find_order(context, msg):
    state = context.state('order').unpack(Order)
    response = ResponseMessage()
    if not state:
        response.result = json.dumps({
            'result': 'failure',
            'message': 'Order does not exist.'
        })
    else:

        # Have to assign all like this so they're not casted to string
        response.result = json.dumps({
            'id': state.id,
            'user_id': state.user_id,
            'items': [i.item_id for i in state.items],
            'total_cost': state.total_cost,
            'paid': state.paid,
            'intent': state.intent
        })

        context.state('order').pack(state)

    return response
Exemplo n.º 9
0
def add_item(context, msg):
    state = context.state('order').unpack(Order)
    if not state:
        response = ResponseMessage()
        response.result = json.dumps({
            'result': 'failure',
            'message': 'Order does not exist.'
        })

        return response
    else:
        # call stock service to reduce the stock
        subtract_stock_request = StockRequest()
        subtract_stock_request.request_info.worker_id = msg.request_info.worker_id
        subtract_stock_request.request_info.request_id = msg.request_info.request_id
        subtract_stock_request.subtract_stock.id = msg.add_item.itemId
        subtract_stock_request.subtract_stock.amount = 1
        subtract_stock_request.internal = True
        subtract_stock_request.order_id = state.id

        context.state('order').pack(state)

        context.pack_and_send("stock/stock", str(msg.add_item.itemId),
                              subtract_stock_request)
Exemplo n.º 10
0
def manage_stock(context, request: typing.Union[StockRequest, CreateItemRequest, OrderAddItemStockRequest]):
    # Get the current state.
    item_state: ItemData = context.state('item').unpack(ItemData)

    if isinstance(request, CreateItemRequest):
        item_state = ItemData()
        item_state.id = request.id
        item_state.price = request.price
        item_state.stock = 0

        context.state('item').pack(item_state)
        #logger.debug(f'Created new item with id {request.id}')

        response = ResponseMessage()
        response.result = json.dumps({'item_id': item_state.id})

    elif isinstance(request, OrderAddItemStockRequest):
        response = None
        if item_state is None:
            pass
        else:
            item_state.stock += request.amount
            context.state('item').pack(item_state)

    elif isinstance(request, StockRequest):

        # If the item state is None we return an error
        if item_state is None:
            # Item does not exist yet. Return error.
            if not request.internal:
                response = ResponseMessage()
                response.result = json.dumps({'result': 'not_found'})
            else:
                response = StockResponse()
                response.item_id = request.subtract_stock.id
                response.result = 'failure'

        else:
            # check which field we have
            msg_type = request.WhichOneof('message')
            #logger.debug(f'Got message of type {msg_type}')

            if msg_type == "find_item":
                response = ResponseMessage()
                response.result = json.dumps(
                    {'id:': item_state.id, 'price': item_state.price, 'stock': item_state.stock})

                context.state('item').pack(item_state)

            elif msg_type == "subtract_stock":
                new_amount = item_state.stock - request.subtract_stock.amount

                if not request.internal:
                    response = ResponseMessage()
                else:
                    response = StockResponse()

                if new_amount >= 0:
                    item_state.stock -= request.subtract_stock.amount

                    context.state('item').pack(item_state)

                    if not request.internal:
                        response.result = json.dumps({'result': 'success', 'item_id': item_state.id})
                    else:
                        # Include the item id and price
                        response.price = item_state.price
                        response.item_id = item_state.id
                        response.result = 'success'
                else:
                    if not request.internal:
                        response.result = json.dumps({'result': 'stock too low', 'item_id': item_state.id})
                    else:
                        response.price = item_state.price
                        response.item_id = item_state.id
                        response.result = 'failure'

            elif msg_type == "add_stock":
                item_state.stock += request.add_stock.amount
                context.state('item').pack(item_state)

                # send the response.
                response = ResponseMessage()
                response.result = json.dumps({'result': 'success', 'item_id': item_state.id})

    if response:
        # Use the same request id in the message body
        # and use the request worker_id as key of the message

        if not request.internal:
            response.request_id = request.request_info.request_id
            # create the egress message and send it to the
            # users/out egress
            egress_message = kafka_egress_record(
                topic=STOCK_EVENTS_TOPIC,
                key=request.request_info.worker_id,
                value=response
            )
            context.pack_and_send_egress("stock/out", egress_message)
        else:
            response.request_info.request_id = request.request_info.request_id
            response.request_info.worker_id = request.request_info.worker_id

            context.pack_and_send("orders/order", str(request.order_id), response)
Exemplo n.º 11
0
def payments_pay(context,
                 request: typing.Union[PaymentRequest, UserPayRequest, Order,
                                       OrdersPayFind, UserPayResponse,
                                       OrderPaymentCancelReply]):

    # Incoming from Orders
    if isinstance(request, Order):
        # If intent == PAY, request is initiated by Orders

        if request.intent == Order.Intent.PAY:
            user_pay_request = set_worker_and_request_ids(
                request, UserPayRequest())
            user_pay_request.order_id = request.id
            user_pay_request.amount = request.total_cost
            context.pack_and_send("users/user", str(request.user_id),
                                  user_pay_request)

        # If intent == CANCEL or STATUS, this is a reply message to an earlier request
        elif request.intent == Order.Intent.CANCEL:

            if not request.paid:
                # Payment cannot be cancelled cause it is not paid
                response = ResponseMessage()
                response.request_id = request.request_info.request_id
                response.result = json.dumps({'result': 'failure'})
                send_response(context, response,
                              request.request_info.worker_id)

            # Otherwise send request to user to subtract the amount
            elif request.paid:
                user_pay_request = set_worker_and_request_ids(
                    request, UserCancelPayRequest())
                user_pay_request.order_id = request.order_id
                user_pay_request.amount = request.total_cost
                context.pack_and_send("users/user", request.user_id,
                                      user_pay_request)

        elif request.intent == Order.Intent.STATUS:
            response = ResponseMessage()
            response.request_id = request.request_info.request_id
            response.result = json.dumps({
                'paid': True
            }) if request.paid else json.dumps({'paid': False})
            send_response(context, response, request.request_info.worker_id)

    # Incoming from Flask
    elif isinstance(request, PaymentRequest):

        if request.request_type == PaymentRequest.RequestType.CANCEL:
            order_payment_cancel_request = set_worker_and_request_ids(
                request, OrderPaymentCancel())
            order_payment_cancel_request.order_id = request.order_id
            context.pack_and_send("orders/order", request.order_id,
                                  order_payment_cancel_request)

        elif request.request_type == PaymentRequest.RequestType.STATUS:
            orders_pay_find_request = set_worker_and_request_ids(
                request, OrdersPayFind())
            orders_pay_find_request.order_id = request.order_id
            context.pack_and_send("orders/order", request.order_id,
                                  orders_pay_find_request)

    # Reply from Users
    elif isinstance(request, UserPayResponse):

        payment_status = set_worker_and_request_ids(request, PaymentStatus())
        payment_status.order_id = request.order_id
        payment_status.actually_paid = request.success

        context.pack_and_send("orders/order", request.order_id, payment_status)

    # Reply from Users
    elif isinstance(request, OrderPaymentCancelReply):
        response = ResponseMessage()
        response.request_id = request.request_info.request_id
        response.result = json.dumps({
            'result': 'success'
        }) if request.success else json.dumps({'result': 'failure'})
        send_response(context, response, request.request_info.worker_id)
Exemplo n.º 12
0
def operate_user(context,
                 request: typing.Union[UserPayRequest, UserCancelPayRequest,
                                       UserRequest, CreateUserRequest]):
    """ Does all the operations with a single user

    Has the state of a user in a UserData() object that includes
    its id and credit under the name 'user' """

    # Get the current state for a given user
    # could have to handle the state not existing for this user
    state: UserData = context.state('user').unpack(UserData)

    response = None

    # ----------------------------------------
    # Messages from the payment endpoint
    # ----------------------------------------

    if isinstance(request, UserPayRequest):

        #logger.debug('Received request to decrement user credit')
        # calculate if the credit is enough to pay for the product
        # get the credit
        response = UserPayResponse()
        response.order_id = request.order_id

        # copy the information of the request_info
        response = copy_request_info(request, response)

        # if the user exists then do the checks
        if state:
            # see whether we should return success or failure
            if state.credit - request.amount < 0:
                response.success = False
            else:
                state.credit -= request.amount
                response.success = True

        else:
            response.success = False

        # pack the state
        context.state('user').pack(state)

        # respond to the payment service
        context.pack_and_reply(response)
        return

    elif isinstance(request, UserCancelPayRequest):

        #logger.debug('Received request to cancel a payment')
        # add the amount specified to the user credit
        response = UserPayResponse()
        response.order_id = request.order_id

        # copy the information
        response = copy_request_info(request, response)

        if state:
            state.credit += request.amount
            # reply
            response.success = True

        else:
            response.success = False

        # pack the state
        context.state('user').pack(state)

        # reply to the sender function
        context.pack_and_reply(response)
        return

    # -------------------------------------
    # Interaction with the user endpoint
    # -------------------------------------

    elif isinstance(request, CreateUserRequest):
        # we are given the uuid in the message so that's already done
        state = UserData()
        state.id = request.id
        state.credit = 0

        #logger.debug(f'Created new user with id {request.id}')
        context.state('user').pack(state)

        response = ResponseMessage()
        response.result = json.dumps({'user_id': state.id})

    elif isinstance(request, UserRequest):

        # check which field we have
        msg_type = request.WhichOneof('message')
        #logger.debug(f'Got message of type {msg_type}')

        # If the state is None we return an error
        if not state:
            response = ResponseMessage()
            response.result = json.dumps(
                {'result': 'failure: user does not exist'})

        else:
            # If the state exists we then operate with it
            if msg_type == 'find_user':

                response = ResponseMessage()
                response.result = json.dumps({
                    'user_id': state.id,
                    'credit': state.credit
                })
                # pack the state
                context.state('user').pack(state)

            elif msg_type == 'remove_user':
                del context['user']

                response = ResponseMessage()
                response.result = json.dumps({'result': 'success'})

            elif msg_type == 'add_credit':
                # Update the credit and save state
                state.credit += request.add_credit.amount
                context.state('user').pack(state)

                # send the response
                response = ResponseMessage()
                response.result = json.dumps({'result': 'success'})

            elif msg_type == 'subtract_credit':
                # try to subtract the amount from the user credit
                new_amount = state.credit - request.subtract_credit.amount
                response = ResponseMessage()

                if new_amount >= 0:
                    state.credit -= request.subtract_credit.amount
                    context.state('user').pack(state)

                    response.result = json.dumps({'result': 'success'})

                else:
                    response.result = json.dumps({'result': 'failure'})

    else:
        logger.error('Received unknown message type!')

    # respond if needed
    if response:
        # Use the same request id in the message body
        # and use the request worker_id as key of the message

        response.request_id = request.request_info.request_id

        # create the egress message and send it to the
        # users/out egress
        egress_message = kafka_egress_record(
            topic=USER_EVENTS_TOPIC,
            key=request.request_info.worker_id,
            value=response)

        context.pack_and_send_egress("users/out", egress_message)