async def push_self_monitoring_time_series(context: SfmContext, time_series: Dict):
    try:
        context.log(f"Pushing self monitoring time series to GCP Monitor...")
        await create_metric_descriptors_if_missing(context)
        for single_series in batch_time_series(time_series):
            await push_single_self_monitoring_time_series(context, False, single_series)
    except Exception as e:
        context.log(f"Failed to push self monitoring time series, reason is {type(e).__name__} {e}")
async def delete_metric_descriptor(context: SfmContext, metric_type: str):
    context.log(f"Removing old descriptor for '{metric_type}'")
    response = await context.gcp_session.request(
        "DELETE",
        url=f"https://monitoring.googleapis.com/v3/projects/{context.project_id_owner}/metricDescriptors/{metric_type}",
        headers={"Authorization": f"Bearer {context.token}"}
    )
    if response.status != 200:
        response_body = await response.json()
        context.log(f"Failed to remove descriptor for '{metric_type}' due to '{response_body}'")
async def create_metric_descriptor(context: SfmContext, metric_descriptor: Dict, metric_type: str):
    context.log(f"Creating missing metric descriptor for '{metric_type}'")
    response = await context.gcp_session.request(
        "POST",
        url=f"https://monitoring.googleapis.com/v3/projects/{context.project_id_owner}/metricDescriptors",
        data=json.dumps(metric_descriptor),
        headers={"Authorization": f"Bearer {context.token}"}
    )

    if response.status > 202:
        response_body = await response.json()
        context.log(f"Failed to create descriptor for '{metric_type}' due to '{response_body}'")
async def create_metric_descriptors_if_missing(context: SfmContext):
    try:
        dynatrace_metrics_descriptors = await context.gcp_session.request(
            'GET',
            url=f"https://monitoring.googleapis.com/v3/projects/{context.project_id_owner}/metricDescriptors",
            params=[('filter', f'metric.type = starts_with("{SELF_MONITORING_METRIC_PREFIX}")')],
            headers={"Authorization": f"Bearer {context.token}"}
        )
        dynatrace_metrics_descriptors_json = await dynatrace_metrics_descriptors.json()
        existing_metric_types = {metric.get("type", ""): metric for metric in dynatrace_metrics_descriptors_json.get("metricDescriptors", [])}
        for metric_type, metric_descriptor in context.sfm_metric_map.items():
            existing_metric_descriptor = existing_metric_types.get(metric_type, None)
            if existing_metric_descriptor:
                await replace_metric_descriptor_if_required(context,
                                                            existing_metric_descriptor,
                                                            metric_descriptor,
                                                            metric_type)
            else:
                await create_metric_descriptor(context, metric_descriptor, metric_type)
    except Exception as e:
        context.log(f"Failed to create self monitoring metrics descriptors, reason is {type(e).__name__} {e}")
async def push_self_monitoring_time_series(context: SfmContext, time_series: Dict, is_retry: bool = False):
    try:
        context.log(f"Pushing self monitoring time series to GCP Monitor...")
        await create_metric_descriptors_if_missing(context)

        self_monitoring_response = await context.gcp_session.request(
            "POST",
            url=f"https://monitoring.googleapis.com/v3/projects/{context.project_id_owner}/timeSeries",
            data=json.dumps(time_series),
            headers={"Authorization": "Bearer {token}".format(token=context.token)}
        )
        status = self_monitoring_response.status
        if status == 500 and not is_retry:
            context.log("GCP Monitor responded with 500 Internal Error, it may occur when metric descriptor is updated. Retrying after 5 seconds")
            await asyncio.sleep(5)
            await push_self_monitoring_time_series(context, time_series, True)
        elif status != 200:
            self_monitoring_response_json = await self_monitoring_response.json()
            context.log(f"Failed to push self monitoring time series, error is: {status} => {self_monitoring_response_json}")
        else:
            context.log(f"Finished pushing self monitoring time series to GCP Monitor")
        self_monitoring_response.close()
    except Exception as e:
        context.log(f"Failed to push self monitoring time series, reason is {type(e).__name__} {e}")