async def _update_credential(self, user, auth_header):
        ctx = RequestContext(auth_header, current_user=user)
        credential = await _fetch_credential_data(auth_header,
                                                  self.kvstore_client)

        if credential.session_type == constants.SPLUNK_SESSION_TOKEN_TYPE:
            temporary_session_key = await get_splunk_cookie(
                request_context=ctx,
                async_splunk_client=self.splunk_client,
                username=ctx.auth_header.username,
                password=ctx.auth_header.password)

            credential.session_key = temporary_session_key

        credential.last_update_time = get_current_timestamp_str()

        LOGGER.debug("flush update credentials")
        credential_post_response = await self.kvstore_client.async_kvstore_post_request(
            collection=constants.SUBSCRIPTION_CREDENTIALS_COLLECTION_NAME,
            data=credential.to_json(),
            owner=ctx.current_user,
            key_id=constants.SUBSCRIPTION_CREDENTIAL_GLOBAL,
            auth_header=auth_header)

        if credential_post_response.code != HTTPStatus.OK:
            error_message = await credential_post_response.text()
            raise SpacebridgeError(error_message,
                                   status_code=credential_post_response.code)
        LOGGER.debug("Updated credentials for username=%s", user)
def build_subscription_search(request_context,
                              dashboard_id,
                              search_defn,
                              search_query,
                              input_tokens,
                              parent_id,
                              search_key,
                              search_type,
                              search_type_id,
                              shard_id,
                              sid,
                              visualization_type=None,
                              trellis_enabled=False,
                              trellis_split_by=None):
    try:
        # Dashboard Definition refresh will override and refresh_interval
        refresh_interval_seconds = refresh_to_seconds(search_defn.refresh)

        # extract username and password
        query = search_query
        earliest_time = search_defn.earliest
        latest_time = search_defn.latest
        sample_ratio = search_defn.sample_ratio
        ref = search_defn.ref
        base = search_defn.base

        next_update_time = get_expiration_timestamp_str(
            ttl_seconds=refresh_interval_seconds)
        last_update_time = get_current_timestamp_str()
        # Create Search object
        subscription_search = SubscriptionSearch(
            _key=search_key,
            dashboard_id=dashboard_id,
            search_type_id=search_type_id,
            search_type=search_type,
            owner=request_context.current_user,
            ref=ref,
            base=base,
            sid=sid,
            query=query,
            parent_search_key=parent_id,
            earliest_time=earliest_time,
            latest_time=latest_time,
            sample_ratio=sample_ratio,
            refresh_interval_seconds=refresh_interval_seconds,
            next_update_time=next_update_time,
            last_update_time=last_update_time,
            input_tokens=input_tokens,
            shard_id=shard_id,
            visualization_type=visualization_type,
            trellis_enabled=trellis_enabled,
            trellis_split_by=trellis_split_by)
    except Exception as e:
        LOGGER.exception("Failed to build subscription_search")
        raise e

    return subscription_search
def process_tv_captain_url_request(request_context,
                                   client_single_request,
                                   single_server_response,
                                   async_client_factory):
    """
    This method processes captain url requests from drone mode TVs.
    It is called when picking a captain when a grid request is activated, and
    reelecting a captain when the captain dies.

    :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
    """
    captain_url_request = client_single_request.tvCaptainUrlRequest
    update_flag = captain_url_request.updateFlag
    captain_id = client_single_request.tvCaptainUrlRequest.captainId
    async_kvstore_client = async_client_factory.kvstore_client()

    # fetch tv config for captain id
    tvs = yield get_drone_mode_tvs(request_context,
                                   async_kvstore_client,
                                   device_ids=[captain_id])
    if not tvs:
        raise SpacebridgeApiRequestError('Invalid captain id provided. captain_id={}'.format(captain_id),
                                         status_code=http.BAD_REQUEST)
    tv = tvs[0]

    LOGGER.debug('tvs in captain url request = %s', tvs)
    tv_config = TVConfig(**tv)

    # if we're updating the captain url, or if we're trying to be elected
    # captain and no one is yet, we do the same thing
    if not (tv_config.captain_url or tv_config.captain_id) or update_flag:
        device_ids = tv_config.tv_grid['device_ids']
        captain_url = captain_url_request.captainUrl
        timestamp = get_current_timestamp_str()
        yield update_grid_members(request_context,
                                  async_kvstore_client,
                                  device_ids,
                                  captain_id,
                                  captain_url,
                                  timestamp)
        yield process_subscriptions(request_context,
                                    async_client_factory,
                                    tv_device_ids=device_ids)

    else:
        LOGGER.debug('captain url already set for device_id=%s, doing nothing', tv_config.device_id)
    single_server_response.tvCaptainUrlResponse.SetInParent()
def create_subscription_credentials(request_context, subscription_id,
                                    async_client_factory):
    async_kvstore_client = async_client_factory.kvstore_client()
    async_splunk_client = async_client_factory.splunk_client()

    if isinstance(request_context.auth_header, JWTAuthHeader):
        LOGGER.debug("JWTAuthHeader detected. Setting session_key_type = %s",
                     constants.JWT_TOKEN_TYPE)
        session_type = constants.JWT_TOKEN_TYPE
        session_key = request_context.auth_header.token
    else:
        LOGGER.debug(
            "SplunkAuthHeader detected. Setting session_key_type = %s",
            constants.SPLUNK_SESSION_TOKEN_TYPE)
        session_type = constants.SPLUNK_SESSION_TOKEN_TYPE
        session_key = yield get_splunk_cookie(
            request_context=request_context,
            async_splunk_client=async_splunk_client,
            username=request_context.auth_header.username,
            password=request_context.auth_header.password)

    now = get_current_timestamp_str()

    auth = SubscriptionCredential(subscription_id=subscription_id,
                                  session_key=session_key,
                                  session_type=session_type,
                                  shard_id=request_context.shard_id,
                                  last_update_time=now,
                                  _key=subscription_id)

    # create subscription and return _key
    response = yield async_kvstore_client.async_kvstore_post_request(
        owner=request_context.current_user,
        collection=constants.SUBSCRIPTION_CREDENTIALS_COLLECTION_NAME,
        data=auth.to_json(),
        auth_header=request_context.auth_header)

    if response.code == http.OK or response.code == http.CREATED or response.code == http.CONFLICT:
        LOGGER.debug("Subscription Created. subscription_id=%s",
                     auth.subscription_id)
        defer.returnValue(auth)
    else:
        error = yield response.text()
        error_message = "Failed to create Subscription credentials. status_code={}, error={}".format(
            response.code, error)
        raise SpacebridgeApiRequestError(error_message)
    async def _batch_extend_subs(self, auth_header, subscription_ids):

        LOGGER.debug("Updating subscriptions=%s", len(subscription_ids))

        if not subscription_ids:
            return

        containedin = build_containedin_clause(constants.KEY,
                                               list(subscription_ids))
        params = {constants.QUERY: json.dumps(containedin)}

        response = await self.kvstore_client.async_kvstore_get_request(
            collection=constants.SUBSCRIPTIONS_COLLECTION_NAME,
            params=json.dumps(params),
            auth_header=auth_header)

        await _ensure_valid_response(response, [HTTPStatus.OK],
                                     "Failed to retrieve subscriptions")

        subscriptions = await response.json()

        subscriptions = [
            Subscription.from_json(subscription_json)
            for subscription_json in subscriptions
        ]

        for subscription in subscriptions:
            subscription.expired_time = get_expiration_timestamp_str(
                ttl_seconds=subscription.ttl_seconds)
            subscription.last_update_time = get_current_timestamp_str()

        subscriptions = [
            subscription.__dict__ for subscription in subscriptions
        ]

        await self.kvstore_client.async_batch_save_request(
            auth_header=auth_header,
            collection=constants.SUBSCRIPTIONS_COLLECTION_NAME,
            entries=subscriptions)
async def process_pubsub_subscription(system_auth_header, encryption_context, async_spacebridge_client,
                                      async_kvstore_client, async_splunk_client, search_context, subscription_update_ids):
    """
    :param system_auth_header:
    :param encryption_context:
    :param async_spacebridge_client:
    :param async_kvstore_client:
    :param async_splunk_client:
    :param search_context:
    :param dependant_searches:
    :return:
    """
    subscription_search = search_context.search

    credentials = search_context.subscription_credentials

    search_updates = search_context.search_updates

    user_subscriptions = search_context.subscriptions

    dependant_searches = search_context.dependant_search_counts

    LOGGER.debug(
        "Found valid subscribers, search_key=%s, user_subscriber_count=%s, search_updates=%s",
        subscription_search.key(),
        len(user_subscriptions),
        search_updates)

    input_tokens = load_input_tokens(subscription_search.input_tokens)

    if not subscription_search.sid:
        LOGGER.info("Pubsub search has no sid, search_key=%s", subscription_search.key())
        return JobResult(False)

    job_status = await get_search_job_content(system_auth_header, subscription_search.owner, SPACEBRIDGE_APP_NAME,
                                              subscription_search.sid, async_splunk_client)

    LOGGER.debug("Search job status, search_key=%s, job=%s", subscription_search.key(), job_status)
    dependant_search_count = dependant_searches[subscription_search.key()]

    LOGGER.debug("Search job dependendants search_key=%s, user_subscriptions=%s, depdendant_search_count=%s",
                 subscription_search.key(), len(user_subscriptions), dependant_search_count)
    if not job_status and (len(user_subscriptions) > 0 or dependant_search_count > 0):
        job_status = await _handle_expired_sid(system_auth_header, subscription_search,
                                               credentials, input_tokens,
                                               async_splunk_client, async_kvstore_client)

    if not job_status:
        LOGGER.warn("Job status could not be retrieved, search_key=%s, sid=%s",
                    subscription_search.key(), subscription_search.sid)
        return JobResult(False)

    new_subscription_update_ids = {}
    # only send updates if the job was still running the last time we saw it
    if len(user_subscriptions) > 0:
        LOGGER.debug("Broadcast Data Updates: search_key=%s, updates=%s", subscription_search.key(), search_updates)
        new_subscription_update_ids = await _broadcast_data_update(system_auth_header, subscription_search,
                                                                   user_subscriptions, search_updates,
                                                                   input_tokens, encryption_context, job_status,
                                                                   async_spacebridge_client, async_kvstore_client,
                                                                   async_splunk_client, subscription_update_ids)

    update_job_status(subscription_search, job_status)

    if user_subscriptions or dependant_search_count:
        LOGGER.debug("Search has subscribers search_key=%s, subscriber_count=%s, dependant_search_count=%s",
                     subscription_search.key(), len(user_subscriptions), dependant_search_count)

        await _refresh_search_job_if_expired(subscription_search, credentials, input_tokens,
                                             async_splunk_client, async_kvstore_client)

        subscription_search.last_update_time = get_current_timestamp_str()

    LOGGER.debug("Persisting search job state, search_key=%s, job_status=%s",
                 subscription_search.key(), job_status)

    return JobResult(True, subscription_search, new_subscription_update_ids)
async def create_subscription(request_context,
                              ttl_seconds,
                              search_key,
                              shard_id,
                              async_splunk_client,
                              async_kvstore_client,
                              visualization_id,
                              id_gen=uuid4):
    """
    Create a visualization subscription object in kvstore collection [subscriptions]
    :param request_context:
    :param ttl_seconds:
    :param search_key:
    :param async_splunk_client:
    :param async_kvstore_client:
    :return:
    """
    # extract params for subscription
    subscription_id = str(id_gen())

    device_id = request_context.device_id
    expiration_time = get_expiration_timestamp_str(ttl_seconds=ttl_seconds)

    if isinstance(request_context.auth_header, JWTAuthHeader):
        LOGGER.debug("JWTAuthHeader detected. Setting session_key_type = %s",
                     JWT_TOKEN_TYPE)
        session_type = JWT_TOKEN_TYPE
        session_key = request_context.auth_header.token
    else:
        LOGGER.debug(
            "SplunkAuthHeader detected. Setting session_key_type = %s",
            SPLUNK_SESSION_TOKEN_TYPE)
        session_type = SPLUNK_SESSION_TOKEN_TYPE
        session_key = await get_splunk_cookie(
            request_context=request_context,
            async_splunk_client=async_splunk_client,
            username=request_context.auth_header.username,
            password=request_context.auth_header.password)

    now = get_current_timestamp_str()

    # Create Subscription object
    subscription = Subscription(_key=subscription_id,
                                ttl_seconds=ttl_seconds,
                                device_id=device_id,
                                subscription_key=search_key,
                                expired_time=expiration_time,
                                shard_id=shard_id,
                                user=request_context.current_user,
                                visualization_id=visualization_id,
                                last_update_time=now)

    auth = SubscriptionCredential(subscription_id=subscription_id,
                                  session_key=session_key,
                                  session_type=session_type,
                                  shard_id=request_context.shard_id,
                                  last_update_time=now,
                                  _key=SUBSCRIPTION_CREDENTIAL_GLOBAL)

    # TODO check if gather wworks as expected
    await asyncio.gather(
        _create_subscription(request_context, subscription,
                             async_kvstore_client),
        _create_subscription_credentials(request_context, auth,
                                         async_kvstore_client))

    LOGGER.debug("Subscription created. subscription_id=%s, search_key=%s",
                 subscription_id, search_key)
    return subscription_id
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_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)
def process_tv_config_bulk_set_request(request_context,
                                       client_single_request,
                                       single_server_response,
                                       async_client_factory):
    """
    Bulk tv config set request.  Used to bulk send tv config set requests
    when we're setting a grid configuration

    :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_protos = client_single_request.tvConfigBulkSetRequest.tvConfig
    is_token_update = client_single_request.tvConfigBulkSetRequest.isTokenUpdate
    LOGGER.debug('tv_config_protos=%s', tv_config_protos)
    device_ids = {proto.device_id for proto in tv_config_protos}
    timestamp = get_current_timestamp_str()
    warnings = yield validate_devices(device_ids, request_context, async_kvstore_client)
    # Before setting configs, we need to check if
    # we're setting configs on tvs that are currently in
    # grid configurations, so we need to fetch them.
    # This logic is ignored if we're doing a token update

    existing_grids_to_update = set()
    if not is_token_update:
        tvs = yield get_drone_mode_tvs(request_context,
                                       async_kvstore_client,
                                       device_ids=device_ids)
        if tvs:
            requests = []
            for tv in tvs:
                existing_config = TVConfig(**tv)
                # If it's a grid, we remove it from the existing grid list
                if has_grid(tv, is_json=True):
                    grid_device_ids = existing_config.tv_grid[constants.DEVICE_IDS]
                    existing_grids_to_update.update(grid_device_ids)
                    request = remove_from_grid(tv_config=existing_config,
                                               device_ids=grid_device_ids,
                                               request_context=request_context,
                                               async_kvstore_client=async_kvstore_client,
                                               timestamp=timestamp)
                    requests.append(request)

            if requests:
                exceptions = []
                responses = yield defer.DeferredList(requests, consumeErrors=True)
                for response in responses:
                    if isinstance(response[1], Failure):
                        exceptions.append(response[1])
                LOGGER.debug('Finished updating modified tvs')
                if exceptions:
                    LOGGER.error('Encountered exceptions updating tvs, e=%s', exceptions)
                    # if we couldn't update any of the existing configs, we should not continue the request
                    if len(exceptions) == len(requests):
                        raise SpacebridgeApiRequestError('Unable to update all existing tv configs',
                                                         status_code=http.INTERNAL_SERVER_ERROR)



    post_data = []
    for tv_config_proto in tv_config_protos:
        tv_config = TVConfig()
        tv_config.from_protobuf(tv_config_proto)

        # 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
        post_data.append(tv_config_json)
    LOGGER.debug('post_data=%s', post_data)
    updated_keys = yield async_kvstore_client.async_batch_save_request(
        request_context.system_auth_header,
        constants.DRONE_MODE_TVS_COLLECTION_NAME,
        post_data,
        owner=request_context.current_user)
    ids_to_update = {urlsafe_b64_to_b64(key) for key in updated_keys}
    ids_to_update.update(existing_grids_to_update)
    ids_to_update = list(ids_to_update)

    LOGGER.debug('ids_to_update=%s', ids_to_update)
    yield process_subscriptions(request_context,
                                async_client_factory,
                                tv_device_ids=ids_to_update)
    if warnings:
        single_server_response.tvConfigBulkSetResponse.warnings.extend(warnings)
    single_server_response.tvConfigBulkSetResponse.SetInParent()
Beispiel #12
0
    def post(self, request):
        """
        Creates/updates a subscription

        Request Parameters
            json version of a subscription collection object. Contains required fields
                _key (required for update, must be empty for create)
                ttl_seconds
                subscription_key
                subscription_type
                device_id
                shard_id
                user

            Example:
                {
                    "ttl_seconds": "10",
                    "subscription_key": "key"
                    "subscription_type": "Splunk TV",
                    "device_id": "device_id",
                    "shard_id": "shard_id",
                    "user": "******",
                }

        Returns:
            id of created/update subscription

            Example:
                {
                    "message": "5f04e4d1ed7a6aab3e6f2e91",
                    "status": 201
                }
        """
        session_key = request[constants.SESSION][constants.AUTHTOKEN]
        payload = json.loads(request[constants.PAYLOAD])
        if not payload:
            return {
                constants.PAYLOAD: {
                    'Error',
                    'You must provide subscription information to update/create'
                },
                constants.STATUS: HTTPStatus.BAD_REQUEST
            }

        kvstore_object = KVStore(
            collection=constants.SUBSCRIPTIONS_COLLECTION_NAME,
            session_key=session_key)

        # Verification of provided fields
        required_fields = (Subscription.TTL_SECONDS,
                           Subscription.SUBSCRIPTION_KEY,
                           Subscription.SUBSCRIPTION_TYPE,
                           Subscription.DEVICE_ID, Subscription.SHARD_ID,
                           Subscription.USER)
        missing_fields = []
        for field in required_fields:
            if field not in payload:
                missing_fields.append(field)

        if missing_fields:
            return {
                constants.PAYLOAD: {
                    'Error': f'Missing field(s) {missing_fields}'
                },
                constants.STATUS: HTTPStatus.BAD_REQUEST
            }

        # Setting default fields
        subscription = Subscription.from_json(payload)
        subscription.expired_time = get_expiration_timestamp_str(
            ttl_seconds=int(subscription.ttl_seconds))
        subscription.version = constants.SUBSCRIPTION_VERSION_2
        subscription.last_update_time = get_current_timestamp_str()

        if subscription.key():  # update case
            return update_subscription(kvstore_object=kvstore_object,
                                       subscription=subscription)
        else:  # create case
            return create_subscription(
                kvstore_object=kvstore_object,
                subscription=subscription,
                session_key=session_key,
            )