Skip to content

Commit

Permalink
Adds User-Agent header to requests (#6065)
Browse files Browse the repository at this point in the history
  • Loading branch information
splch committed Apr 19, 2023
1 parent 79286a1 commit a96a98c
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 17 deletions.
32 changes: 31 additions & 1 deletion cirq-ionq/cirq_ionq/ionq_client.py
Expand Up @@ -17,13 +17,15 @@
import sys
import time
import urllib
import platform
from typing import Any, Callable, cast, Dict, List, Optional
import json.decoder as jd

import requests

import cirq_ionq
from cirq_ionq import ionq_exceptions
from cirq import __version__ as cirq_version

# https://support.cloudflare.com/hc/en-us/articles/115003014512-4xx-Client-Error
# "Cloudflare will generate and serve a 409 response for a Error 1001: DNS Resolution Error."
Expand Down Expand Up @@ -96,7 +98,7 @@ def __init__(
assert max_retry_seconds >= 0, 'Negative retry not possible without time machine.'

self.url = f'{url.scheme}://{url.netloc}/{api_version}'
self.headers = {'Authorization': f'apiKey {api_key}', 'Content-Type': 'application/json'}
self.headers = self.api_headers(api_key)
self.default_target = default_target
self.max_retry_seconds = max_retry_seconds
self.verbose = verbose
Expand Down Expand Up @@ -263,6 +265,34 @@ def list_calibrations(
params['end'] = int((end - epoch).total_seconds() * 1000)
return self._list('calibrations', params, 'calibrations', limit, batch_size)

def api_headers(self, api_key: str):
"""API Headers needed to make calls to the REST API.
Args:
api_key: The key used for authenticating against the IonQ API.
Returns:
dict[str, str]: A dict of :class:`requests.Request` headers.
"""
return {
'Authorization': f'apiKey {api_key}',
'Content-Type': 'application/json',
'User-Agent': self._user_agent(),
}

def _user_agent(self):
"""Generates the user agent string which is helpful in identifying
different tools in the internet. Valid user-agent ionq_client header that
indicates the request is from cirq_ionq along with the system, os,
python,libraries details.
Returns:
str: A string of generated user agent.
"""
cirq_version_string = f'cirq/{cirq_version}'
python_version_string = f'python/{platform.python_version()}'
return f'User-Agent: {cirq_version_string} ({python_version_string})'

def _target(self, target: Optional[str]) -> str:
"""Returns the target if not None or the default target.
Expand Down
97 changes: 81 additions & 16 deletions cirq-ionq/cirq_ionq/ionq_client_test.py
Expand Up @@ -82,6 +82,7 @@ def test_ionq_client_attributes():
assert client.headers == {
'Authorization': 'apiKey to_my_heart',
'Content-Type': 'application/json',
'User-Agent': client._user_agent(),
}
assert client.default_target == 'qpu'
assert client.max_retry_seconds == 10
Expand All @@ -108,7 +109,11 @@ def test_ionq_client_create_job(mock_post):
'shots': '200',
'metadata': {'shots': '200', 'a': '0,1'},
}
expected_headers = {'Authorization': 'apiKey to_my_heart', 'Content-Type': 'application/json'}
expected_headers = {
'Authorization': 'apiKey to_my_heart',
'Content-Type': 'application/json',
'User-Agent': client._user_agent(),
}
mock_post.assert_called_with(
'http://example.com/v0.1/jobs', json=expected_json, headers=expected_headers
)
Expand Down Expand Up @@ -261,7 +266,11 @@ def test_ionq_client_get_job_retry_409(mock_get):
response = client.get_job(job_id='job_id')
assert response == {'foo': 'bar'}

expected_headers = {'Authorization': 'apiKey to_my_heart', 'Content-Type': 'application/json'}
expected_headers = {
'Authorization': 'apiKey to_my_heart',
'Content-Type': 'application/json',
'User-Agent': client._user_agent(),
}
mock_get.assert_called_with('http://example.com/v0.1/jobs/job_id', headers=expected_headers)


Expand All @@ -273,7 +282,11 @@ def test_ionq_client_get_job(mock_get):
response = client.get_job(job_id='job_id')
assert response == {'foo': 'bar'}

expected_headers = {'Authorization': 'apiKey to_my_heart', 'Content-Type': 'application/json'}
expected_headers = {
'Authorization': 'apiKey to_my_heart',
'Content-Type': 'application/json',
'User-Agent': client._user_agent(),
}
mock_get.assert_called_with('http://example.com/v0.1/jobs/job_id', headers=expected_headers)


Expand Down Expand Up @@ -336,7 +349,11 @@ def test_ionq_client_list_jobs(mock_get):
response = client.list_jobs()
assert response == [{'id': '1'}, {'id': '2'}]

expected_headers = {'Authorization': 'apiKey to_my_heart', 'Content-Type': 'application/json'}
expected_headers = {
'Authorization': 'apiKey to_my_heart',
'Content-Type': 'application/json',
'User-Agent': client._user_agent(),
}
mock_get.assert_called_with(
'http://example.com/v0.1/jobs', headers=expected_headers, json={'limit': 1000}, params={}
)
Expand All @@ -350,7 +367,11 @@ def test_ionq_client_list_jobs_status(mock_get):
response = client.list_jobs(status='canceled')
assert response == [{'id': '1'}, {'id': '2'}]

expected_headers = {'Authorization': 'apiKey to_my_heart', 'Content-Type': 'application/json'}
expected_headers = {
'Authorization': 'apiKey to_my_heart',
'Content-Type': 'application/json',
'User-Agent': client._user_agent(),
}
mock_get.assert_called_with(
'http://example.com/v0.1/jobs',
headers=expected_headers,
Expand All @@ -367,7 +388,11 @@ def test_ionq_client_list_jobs_limit(mock_get):
response = client.list_jobs(limit=2)
assert response == [{'id': '1'}, {'id': '2'}]

expected_headers = {'Authorization': 'apiKey to_my_heart', 'Content-Type': 'application/json'}
expected_headers = {
'Authorization': 'apiKey to_my_heart',
'Content-Type': 'application/json',
'User-Agent': client._user_agent(),
}
mock_get.assert_called_with(
'http://example.com/v0.1/jobs', headers=expected_headers, json={'limit': 1000}, params={}
)
Expand All @@ -385,7 +410,11 @@ def test_ionq_client_list_jobs_batches(mock_get):
response = client.list_jobs(batch_size=1)
assert response == [{'id': '1'}, {'id': '2'}, {'id': '3'}]

expected_headers = {'Authorization': 'apiKey to_my_heart', 'Content-Type': 'application/json'}
expected_headers = {
'Authorization': 'apiKey to_my_heart',
'Content-Type': 'application/json',
'User-Agent': client._user_agent(),
}
url = 'http://example.com/v0.1/jobs'
mock_get.assert_has_calls(
[
Expand All @@ -410,7 +439,11 @@ def test_ionq_client_list_jobs_batches_does_not_divide_total(mock_get):
response = client.list_jobs(batch_size=2)
assert response == [{'id': '1'}, {'id': '2'}, {'id': '3'}]

expected_headers = {'Authorization': 'apiKey to_my_heart', 'Content-Type': 'application/json'}
expected_headers = {
'Authorization': 'apiKey to_my_heart',
'Content-Type': 'application/json',
'User-Agent': client._user_agent(),
}
url = 'http://example.com/v0.1/jobs'
mock_get.assert_has_calls(
[
Expand Down Expand Up @@ -463,7 +496,11 @@ def test_ionq_client_cancel_job(mock_put):
response = client.cancel_job(job_id='job_id')
assert response == {'foo': 'bar'}

expected_headers = {'Authorization': 'apiKey to_my_heart', 'Content-Type': 'application/json'}
expected_headers = {
'Authorization': 'apiKey to_my_heart',
'Content-Type': 'application/json',
'User-Agent': client._user_agent(),
}
mock_put.assert_called_with(
'http://example.com/v0.1/jobs/job_id/status/cancel', headers=expected_headers
)
Expand Down Expand Up @@ -528,7 +565,11 @@ def test_ionq_client_delete_job(mock_delete):
response = client.delete_job(job_id='job_id')
assert response == {'foo': 'bar'}

expected_headers = {'Authorization': 'apiKey to_my_heart', 'Content-Type': 'application/json'}
expected_headers = {
'Authorization': 'apiKey to_my_heart',
'Content-Type': 'application/json',
'User-Agent': client._user_agent(),
}
mock_delete.assert_called_with('http://example.com/v0.1/jobs/job_id', headers=expected_headers)


Expand Down Expand Up @@ -591,7 +632,11 @@ def test_ionq_client_get_current_calibrations(mock_get):
response = client.get_current_calibration()
assert response == {'foo': 'bar'}

expected_headers = {'Authorization': 'apiKey to_my_heart', 'Content-Type': 'application/json'}
expected_headers = {
'Authorization': 'apiKey to_my_heart',
'Content-Type': 'application/json',
'User-Agent': client._user_agent(),
}
mock_get.assert_called_with(
'http://example.com/v0.1/calibrations/current', headers=expected_headers
)
Expand Down Expand Up @@ -648,7 +693,11 @@ def test_ionq_client_list_calibrations(mock_get):
response = client.list_calibrations()
assert response == [{'id': '1'}, {'id': '2'}]

expected_headers = {'Authorization': 'apiKey to_my_heart', 'Content-Type': 'application/json'}
expected_headers = {
'Authorization': 'apiKey to_my_heart',
'Content-Type': 'application/json',
'User-Agent': client._user_agent(),
}
mock_get.assert_called_with(
'http://example.com/v0.1/calibrations',
headers=expected_headers,
Expand All @@ -668,7 +717,11 @@ def test_ionq_client_list_calibrations_dates(mock_get):
)
assert response == [{'id': '1'}, {'id': '2'}]

expected_headers = {'Authorization': 'apiKey to_my_heart', 'Content-Type': 'application/json'}
expected_headers = {
'Authorization': 'apiKey to_my_heart',
'Content-Type': 'application/json',
'User-Agent': client._user_agent(),
}
mock_get.assert_called_with(
'http://example.com/v0.1/calibrations',
headers=expected_headers,
Expand All @@ -687,7 +740,11 @@ def test_ionq_client_list_calibrations_limit(mock_get):
response = client.list_calibrations(limit=2)
assert response == [{'id': '1'}, {'id': '2'}]

expected_headers = {'Authorization': 'apiKey to_my_heart', 'Content-Type': 'application/json'}
expected_headers = {
'Authorization': 'apiKey to_my_heart',
'Content-Type': 'application/json',
'User-Agent': client._user_agent(),
}
mock_get.assert_called_with(
'http://example.com/v0.1/calibrations',
headers=expected_headers,
Expand All @@ -708,7 +765,11 @@ def test_ionq_client_list_calibrations_batches(mock_get):
response = client.list_calibrations(batch_size=1)
assert response == [{'id': '1'}, {'id': '2'}, {'id': '3'}]

expected_headers = {'Authorization': 'apiKey to_my_heart', 'Content-Type': 'application/json'}
expected_headers = {
'Authorization': 'apiKey to_my_heart',
'Content-Type': 'application/json',
'User-Agent': client._user_agent(),
}
url = 'http://example.com/v0.1/calibrations'
mock_get.assert_has_calls(
[
Expand All @@ -733,7 +794,11 @@ def test_ionq_client_list_calibrations_batches_does_not_divide_total(mock_get):
response = client.list_calibrations(batch_size=2)
assert response == [{'id': '1'}, {'id': '2'}, {'id': '3'}]

expected_headers = {'Authorization': 'apiKey to_my_heart', 'Content-Type': 'application/json'}
expected_headers = {
'Authorization': 'apiKey to_my_heart',
'Content-Type': 'application/json',
'User-Agent': client._user_agent(),
}
url = 'http://example.com/v0.1/calibrations'
mock_get.assert_has_calls(
[
Expand Down

0 comments on commit a96a98c

Please sign in to comment.