def fetch_tv_bookmark(bookmark_name,
                      request_context,
                      async_kvstore_client,
                      error_if_empty=False):
    """
    Function to fetch tv bookmark data from kvstore
    :param bookmark_name: tv bookmark name
    :param request_context: request context used to make kvstore requests
    :param async_kvstore_client: async client used to make kvstore requests
    :param error_if_empty: Whether or not to error out if there is no data returned
    """
    query = {constants.NAME: bookmark_name}

    response = yield async_kvstore_client.async_kvstore_get_request(
        collection=constants.TV_BOOKMARK_COLLECTION_NAME,
        owner=request_context.current_user,
        params={
            constants.QUERY: json.dumps(query),
            constants.LIMIT: 1
        },
        auth_header=request_context.auth_header)

    yield check_and_raise_error(response, request_context, 'Get TV Bookmark')
    bookmark_json = yield response.json()
    if not bookmark_json and error_if_empty:
        raise SpacebridgeApiRequestError(
            'Bookmark with name={} could not be found'.format(bookmark_name),
            status_code=http.NOT_FOUND)
    if bookmark_json:
        defer.returnValue(bookmark_json[0])
    defer.returnValue({})
def process_tv_bookmark_delete_request(request_context,
                                       client_single_request,
                                       single_server_response,
                                       async_kvstore_client):
    """
    This method will process a tvBookmarkDeleteRequest.

    :param request_context: Used to authenticate kvstore requests
    :param client_single_request: client request object protobuf
    :param single_server_response: server response object protobuf
    :param async_kvstore_client: kvstore client used to make kvstore requests
    """

    tv_bookmark_delete_request = client_single_request.tvBookmarkDeleteRequest
    bookmark_name = tv_bookmark_delete_request.bookmarkName

    params = {constants.QUERY: json.dumps({constants.NAME: bookmark_name})}

    response = yield async_kvstore_client.async_kvstore_delete_request(
        collection=constants.TV_BOOKMARK_COLLECTION_NAME,
        params=params,
        owner=request_context.current_user,
        auth_header=request_context.system_auth_header)

    yield check_and_raise_error(response, request_context, 'Delete TV Bookmark')

    single_server_response.tvBookmarkDeleteResponse.SetInParent()
    LOGGER.info('Successful TV Bookmark Delete Request for bookmark name=%s', bookmark_name)
def process_tv_bookmark_set_request(request_context,
                                    client_single_request,
                                    single_server_response,
                                    async_kvstore_client):
    """
    This method will process a tvBookmarkSetRequest.

    :param request_context: Used to authenticate kvstore requests
    :param client_single_request: client request object protobuf
    :param single_server_response: server response object protobuf
    :param async_kvstore_client: kvstore client used to make kvstore requests
    """

    # save bookmark data to kvstore
    response = yield save_bookmark_to_kvstore(client_single_request,
                                              request_context,
                                              async_kvstore_client)

    yield check_and_raise_error(response,
                                request_context,
                                'Set TV Bookmark',
                                valid_codes=[http.OK, http.CREATED])

    single_server_response.tvBookmarkSetResponse.SetInParent()
    LOGGER.info('Successful TV Bookmark Set Request.')
Exemple #4
0
def get_drone_mode_tvs(request_context,
                       async_kvstore_client,
                       user=None,
                       device_ids=None):
    """Fetch list of tvs from the drone_mode_tvs collection"""

    params = None
    if not user:
        user = request_context.current_user

    if device_ids:
        urlsafe_device_ids = [
            b64_to_urlsafe_b64(device_id) for device_id in device_ids
        ]
        query = build_containedin_clause(constants.KEY, urlsafe_device_ids)

        params = {constants.QUERY: json.dumps(query)}

    response = yield async_kvstore_client.async_kvstore_get_request(
        collection=constants.DRONE_MODE_TVS_COLLECTION_NAME,
        params=params,
        owner=user,
        auth_header=request_context.system_auth_header)

    yield check_and_raise_error(response, request_context,
                                'Get Drone Mode TVs')
    tvs = yield response.json()

    LOGGER.debug('get_drone_mode_tvs returned=%s', tvs)
    defer.returnValue(tvs)
def process_mpc_broadcast_request(request_context,
                                  client_single_request,
                                  single_server_response,
                                  async_client_factory):
    """
    This Method will process a MPC Broadcast Request

    :param request_context: Used to authenticate kvstore requests
    :param client_single_request: client request object protobuf
    :param single_server_response: server response object protobuf
    :param async_client factory: factory class used to generate kvstore and spacebridge clients
    """

    async_kvstore_client = async_client_factory.kvstore_client()
    device_id = client_single_request.startMPCBroadcastRequest.deviceId

    subscriptions = yield fetch_subscriptions(request_context.auth_header,
                                              async_kvstore_client,
                                              user_list=[request_context.current_user],
                                              subscription_type=constants.DRONE_MODE_TV,
                                              device_ids=[device_id])
    if not subscriptions:
        raise SpacebridgeApiRequestError('No active subscriptions for device_id={}'
                                         .format(device_id),
                                         status_code=http.BAD_REQUEST)

    async_spacebridge_client = async_client_factory.spacebridge_client()
    active_subscription = subscriptions[0]
    sodium_client = SodiumClient()
    encryption_context = SplunkEncryptionContext(request_context.system_auth_header.session_token,
                                                 constants.SPACEBRIDGE_APP_NAME,
                                                 sodium_client)
    start_broadcast = StartMPCBroadcast(device_id=device_id)
    subscription_update = DroneModeTVEvent(data_object=start_broadcast,
                                           event_type=TVEventType.MPC_BROADCAST)
    response, subscription_key = yield send_drone_mode_subscription_update(request_context.system_auth_header,
                                                                           active_subscription,
                                                                           subscription_update,
                                                                           encryption_context,
                                                                           async_spacebridge_client,
                                                                           async_kvstore_client)
    yield check_and_raise_error(response, request_context, 'MPC Broadcast Request')
    single_server_response.startMPCBroadcastResponse.SetInParent()

    LOGGER.info('Successfully sent mpc broadcast message to device_id=%s with subscription_key=%s',
                device_id,
                subscription_key)
def process_tv_bookmark_get_request(request_context,
                                    client_single_request,
                                    single_server_response,
                                    async_kvstore_client):
    """
    This method will process a tvBookmarkGetRequest.

    :param request_context: Used to authenticate kvstore requests
    :param client_single_request: client request object protobuf
    :param single_server_response: server response object protobuf
    :param async_kvstore_client: kvstore client used to make kvstore requests
    """

    if client_single_request.tvBookmarkGetRequest.bookmarkName:
        bookmark_name = client_single_request.tvBookmarkGetRequest.bookmarkName
        params = {constants.QUERY: json.dumps({constants.NAME: bookmark_name}), constants.LIMIT: 1}
    else:
        bookmark_name = None
        params = None
    LOGGER.debug('bookmark_name=%s', bookmark_name)

    response = yield async_kvstore_client.async_kvstore_get_request(
        collection=constants.TV_BOOKMARK_COLLECTION_NAME,
        owner=request_context.current_user,
        params=params,
        auth_header=request_context.auth_header)
    yield check_and_raise_error(response, request_context, 'Fetch TV Bookmark')
    tv_bookmarks = yield response.json()
    if not tv_bookmarks and bookmark_name:
        raise SpacebridgeApiRequestError('Could not find any bookmarks for the supplied request',
                                         status_code=http.NOT_FOUND)

    if bookmark_name:
        # Fetch only first element since we limit our query to one, and bookmarks are unique by name
        bookmark = tv_bookmarks[0]
        LOGGER.debug('bookmark=%s', bookmark)
        proto = yield construct_tv_bookmark_proto(bookmark)
        single_server_response.tvBookmarkGetResponse.tvBookmark.extend([proto])
    else:
        proto_list = []
        for bookmark in tv_bookmarks:
            proto = yield construct_tv_bookmark_proto(bookmark)
            proto_list.append(proto)

        single_server_response.tvBookmarkGetResponse.tvBookmark.extend(proto_list)

    LOGGER.info('Successful TV Bookmark Get Request. Bookmark=%s', bookmark_name)
def send_device_update(subscription_update, subscription_type, subscription,
                       request_context, encryption_context,
                       async_spacebridge_client, async_kvstore_client):
    """Function to construct and send a single DroneMode iPad
    TVList subscription event
    :param tv_list: TV list to send in update
    :param subscription: Subscription to update
    :param auth_header: auth heard usedto authenticate kvstore client
    :param encryption context: used to encrypt spacebridge message
    :param async_spacebridge client: used to send spacebridge message
    :param async_kvstore_client: usd to make kvstore request
    """

    response, subscription_key = yield send_drone_mode_subscription_update(
        request_context.system_auth_header, subscription, subscription_update,
        encryption_context, async_spacebridge_client, async_kvstore_client)

    yield check_and_raise_error(
        response, request_context,
        'Send {} Subscription Update'.format(subscription_type))
    LOGGER.debug('subscription_update=%s', subscription_update)
    defer.returnValue((response, subscription_key))
def send_tv_config_update(tv_config, subscription, request_context,
                          encryption_context, async_spacebridge_client,
                          async_kvstore_client):
    """Function to construct and send a single DroneMode TV Config
    subscription update event
    :param tv_config: TV config to update
    :param subscription: Subscription to update
    :param auth_header: auth heard usedto authenticate kvstore client
    :param encryption context: used to encrypt spacebridge message
    :param async_spacebridge client: used to send spacebridge message
    :param async_kvstore_client: usd to make kvstore request
    """
    LOGGER.debug('sending update for device_id=%s', tv_config.device_id)
    subscription_update = DroneModeTVEvent(data_object=tv_config,
                                           event_type=TVEventType.TV_CONFIG)
    response, subscription_key = yield send_drone_mode_subscription_update(
        request_context.system_auth_header, subscription, subscription_update,
        encryption_context, async_spacebridge_client, async_kvstore_client)

    yield check_and_raise_error(response, request_context,
                                'Send TV Subscription Update')
    LOGGER.debug('subscription_update=%s', subscription_update)
    defer.returnValue((response, subscription_key))
def process_subscribe_drone_mode_ipad(request_context,
                                      client_subscription_message,
                                      server_subscription_response,
                                      async_client_factory):
    """
    Process subscribe to drone mode ipad events for a device id

    :param request_context: Used to authenticate kvstore requests
    :param client_single_request: client request object protobuf
    :param single_server_response: server response object protobuf
    :param async_client factory: factory class used to generate kvstore and spacebridge clients
    """

    LOGGER.debug('start of subscribe drone mode ipad')
    async_kvstore_client = async_client_factory.kvstore_client()

    LOGGER.debug('request_context=%s', request_context)
    # base64 encode device id
    device_id = request_context.device_id
    urlsafe_b64encoded_device_id = b64_to_urlsafe_b64(device_id)
    query = {
        constants.QUERY: json.dumps({
            constants.DEVICE_ID: device_id,
        }),
        constants.LIMIT: 1
    }

    # fetch device from registered_devices_table to get device name
    response = yield async_kvstore_client.async_kvstore_get_request(
        collection=constants.REGISTERED_DEVICES_COLLECTION_NAME,
        params=query,
        owner=request_context.current_user,
        auth_header=request_context.auth_header)

    yield check_and_raise_error(response, request_context, 'Fetch registered devices')

    device = yield response.json()
    if device:
        device = device[0]
    else:
        raise SpacebridgeApiRequestError('Invalid device id={}'.format(device_id), status_code=http.BAD_REQUEST)
    device_name = device[constants.DEVICE_NAME]

    # write to ipads collection
    ipad_json = {constants.DEVICE_NAME: device_name}
    response = yield async_kvstore_client.async_kvstore_post_or_update_request(
        constants.DRONE_MODE_IPADS_COLLECTION_NAME,
        json.dumps(ipad_json),
        request_context.system_auth_header,
        key_id=urlsafe_b64encoded_device_id,
        owner=request_context.current_user)

    # construct parameters for subscription creation / updating
    ttl_seconds = client_subscription_message.clientSubscribeRequest.ttlSeconds
    expiration_time = get_expiration_timestamp_str(ttl_seconds=ttl_seconds)
    now = get_current_timestamp_str()
    params = {
        constants.TTL_SECONDS: str(ttl_seconds),
        constants.DEVICE_ID: device_id,
        constants.LAST_UPDATE_TIME: now,
        constants.EXPIRED_TIME: expiration_time,
        constants.USER: request_context.current_user,
        constants.SUBSCRIPTION_TYPE: constants.DRONE_MODE_IPAD
    }
    # delete existing subscriptions
    query = {
        constants.QUERY: json.dumps({
            constants.DEVICE_ID: device_id,
            constants.USER: request_context.current_user,
            constants.SUBSCRIPTION_TYPE: constants.DRONE_MODE_IPAD
        })
    }
    delete_response = yield async_kvstore_client.async_kvstore_delete_request(
        collection=constants.SUBSCRIPTIONS_COLLECTION_NAME,
        params=query,
        owner=constants.NOBODY,
        auth_header=request_context.auth_header)
    yield check_and_raise_error(delete_response, request_context, 'Delete existing ipad subscriptions')

    # create subscription
    subscription_id = yield write_drone_mode_subscription(request_context,
                                                          params,
                                                          async_client_factory)
    server_subscription_response.subscriptionId = subscription_id
    server_subscription_response.serverSubscribeResponse.droneModeiPadSubscribe.SetInParent()
    LOGGER.debug('Successfully wrote drone mode ipad subscription with id=%s', subscription_id)
def process_subscribe_drone_mode_tv(request_context,
                                    client_subscription_message,
                                    server_subscription_response,
                                    async_client_factory):
    """
    Process subscribe to drone mode tv events for a device id

    :param request_context: Used to authenticate kvstore requests
    :param client_single_request: client request object protobuf
    :param single_server_response: server response object protobuf
    :param async_client factory: factory class used to generate kvstore and spacebridge clients

    """

    LOGGER.debug('start of subscribe drone mode tv')
    async_kvstore_client = async_client_factory.kvstore_client()

    # base64 encode device id
    device_id = client_subscription_message.clientSubscribeRequest.droneModeTVSubscribe.deviceId
    encoded_device_id = b64encode_to_str(device_id)
    urlsafe_encoded_device_id = urlsafe_b64encode_to_str(device_id)

    # Grab TV data
    tv_data = yield get_registered_tvs(request_context.auth_header,
                                       request_context.current_user,
                                       async_kvstore_client)

    LOGGER.debug('raw device id=%s, encoded_device_id=%s, urlsafe_encoded_device_id=%s, tv_data=%s',
                 device_id,
                 encoded_device_id,
                 urlsafe_encoded_device_id,
                 tv_data)

    # validate that device is a valid apple tv registered to this user
    valid_device_id = any(device.get(constants.DEVICE_ID) == encoded_device_id for device in tv_data)

    if not valid_device_id:
        error_message = 'Invalid device id={}'.format(encoded_device_id)
        raise SpacebridgeApiRequestError(error_message, status_code=http.BAD_REQUEST)

    # construct parameters for subscription creation / updating
    ttl_seconds = client_subscription_message.clientSubscribeRequest.ttlSeconds
    expiration_time = get_expiration_timestamp_str(ttl_seconds=ttl_seconds)
    now = get_current_timestamp_str()
    params = {
        constants.TTL_SECONDS: str(ttl_seconds),
        constants.DEVICE_ID: encoded_device_id,
        constants.EXPIRED_TIME: expiration_time,
        constants.USER: request_context.current_user,
        constants.LAST_UPDATE_TIME: now,
        constants.SUBSCRIPTION_TYPE: constants.DRONE_MODE_TV
    }


    # delete existing subscriptions
    query = {
        constants.QUERY: json.dumps({
            constants.DEVICE_ID: encoded_device_id,
            constants.USER: request_context.current_user,
            constants.SUBSCRIPTION_TYPE: constants.DRONE_MODE_TV
        })
    }
    delete_response = yield async_kvstore_client.async_kvstore_delete_request(
        collection=constants.SUBSCRIPTIONS_COLLECTION_NAME,
        params=query,
        owner=constants.NOBODY,
        auth_header=request_context.auth_header)

    yield check_and_raise_error(delete_response, request_context, 'Delete existing tv subscriptions')
    # create subscription
    subscription_id = yield write_drone_mode_subscription(request_context,
                                                          params,
                                                          async_client_factory)
    server_subscription_response.subscriptionId = subscription_id
    server_subscription_response.serverSubscribeResponse.droneModeTVSubscribe.deviceId = device_id
    LOGGER.debug('Successfully wrote drone mode tv subscription with id=%s', subscription_id)
def process_tv_interaction_request(request_context,
                                   client_single_request,
                                   single_server_response,
                                   async_client_factory):
    """
    This Method will process a MPC Broadcast Request

    :param request_context: Used to authenticate kvstore requests
    :param client_single_request: client request object protobuf
    :param single_server_response: server response object protobuf
    :param async_client factory: factory class used to generate kvstore and spacebridge clients
    """

    async_kvstore_client = async_client_factory.kvstore_client()
    device_id = client_single_request.tvInteractionRequest.tvInteraction.device_id
    tv_interaction_proto = client_single_request.tvInteractionRequest.tvInteraction
    # determine type of interaction
    interaction_type = TVInteractionType.NONE
    speed = None
    if tv_interaction_proto.HasField(constants.SLIDESHOW_GOTO):
        interaction_type = TVInteractionType.GOTO

    elif tv_interaction_proto.HasField(constants.SLIDESHOW_STOP):
        interaction_type = TVInteractionType.STOP

    elif tv_interaction_proto.HasField(constants.SLIDESHOW_FORWARD):
        interaction_type = TVInteractionType.FORWARD

    elif tv_interaction_proto.HasField(constants.SLIDESHOW_BACK):
        interaction_type = TVInteractionType.BACK

    elif tv_interaction_proto.HasField(constants.SLIDESHOW_SPEED):
        interaction_type = TVInteractionType.SPEED
        speed = tv_interaction_proto.slideshow_speed.speed


    subscriptions = yield fetch_subscriptions(request_context.auth_header,
                                              async_kvstore_client,
                                              user_list=[request_context.current_user],
                                              subscription_type=constants.DRONE_MODE_TV,
                                              device_ids=[device_id])


    if not subscriptions:
        raise SpacebridgeApiRequestError('No active subscriptions for device_id={}'
                                         .format(device_id),
                                         status_code=http.BAD_REQUEST)

    async_spacebridge_client = async_client_factory.spacebridge_client()
    active_subscription = subscriptions[0]
    sodium_client = SodiumClient()
    encryption_context = SplunkEncryptionContext(request_context.system_auth_header.session_token,
                                                 constants.SPACEBRIDGE_APP_NAME,
                                                 sodium_client)
    tv_interaction = TVInteraction(device_id=device_id,
                                   interaction_type=interaction_type,
                                   speed=speed)
    subscription_update = DroneModeTVEvent(data_object=tv_interaction,
                                           event_type=TVEventType.TV_INTERACTION)
    response, subscription_key = yield send_drone_mode_subscription_update(request_context.system_auth_header,
                                                                           active_subscription,
                                                                           subscription_update,
                                                                           encryption_context,
                                                                           async_spacebridge_client,
                                                                           async_kvstore_client)
    yield check_and_raise_error(response, request_context, 'MPC Broadcast Request')
    single_server_response.tvInteractionResponse.SetInParent()

    LOGGER.info('Successfully sent mpc broadcast message to device_id=%s with subscription_key=%s',
                device_id,
                subscription_key)
def process_tv_config_set_request(request_context,
                                  client_single_request,
                                  single_server_response,
                                  async_client_factory):
    """
    This method will process a tvConfigSetRequest.

    :param request_context: Used to authenticate kvstore requests
    :param client_single_request: client request object protobuf
    :param single_server_response: server response object protobuf
    :param async_client factory: factory class used to generate kvstore and spacebridge clients
    """
    async_kvstore_client = async_client_factory.kvstore_client()
    tv_config_set_request = client_single_request.tvConfigSetRequest

    tv_config_proto = tv_config_set_request.tvConfig
    tv_config = TVConfig()
    tv_config.from_protobuf(tv_config_proto)
    timestamp = get_current_timestamp_str()
    ids_to_update = [tv_config.device_id]
    # Check to make sure device is valid before proceeding
    yield validate_devices(set(ids_to_update), request_context, async_kvstore_client)

    # Before setting config, we need to check if
    # we're setting a config on a tv that is currently in
    # a grid configuration, so we need to fetch it.
    tvs = yield get_drone_mode_tvs(request_context,
                                   async_kvstore_client,
                                   device_ids=[tv_config.device_id])

    if tvs:
        tv = tvs[0]
        existing_config = TVConfig(**tv)
        # If it's a grid, we remove it from the existing grid list,
        if has_grid(tv, is_json=True) and not has_grid(tv_config_proto):
            device_ids = ids_to_update = existing_config.tv_grid[constants.DEVICE_IDS]
            yield remove_from_grid(tv_config=existing_config,
                                   device_ids=device_ids,
                                   request_context=request_context,
                                   async_kvstore_client=async_kvstore_client,
                                   timestamp=timestamp)

    # set timestamp here
    tv_config.timestamp = timestamp
    tv_config_json = json.loads(tv_config.to_json())

    # delete device id
    if constants.DEVICE_ID in tv_config_json:
        del tv_config_json[constants.DEVICE_ID]

    LOGGER.debug('tv_config_json=%s', tv_config_json)

    # convert device id to urlsafe b64 encoded to use as kvstore key
    urlsafe_b64encoded_device_id = b64_to_urlsafe_b64(tv_config.device_id)
    tv_config_json[constants.KEY] = urlsafe_b64encoded_device_id

    response = yield async_kvstore_client.async_kvstore_post_or_update_request(
        constants.DRONE_MODE_TVS_COLLECTION_NAME,
        json.dumps(tv_config_json),
        request_context.system_auth_header,
        key_id=urlsafe_b64encoded_device_id,
        owner=request_context.current_user)

    yield check_and_raise_error(response,
                                request_context,
                                'Write TV Config',
                                valid_codes=[http.OK, http.CREATED])

    single_server_response.tvConfigSetResponse.SetInParent()
    # update tv subscription for config we just created
    # (and affected grid device ids, if applicable) as well as
    # all ipads registered to current user
    yield process_subscriptions(request_context,
                                async_client_factory,
                                tv_device_ids=ids_to_update)

    LOGGER.info("Successful TV Config Set Request for device id=%s", tv_config.device_id)