Skip to content

Commit

Permalink
Updated to version 2.1.0:
Browse files Browse the repository at this point in the history
- Added more tests for auth
- Added troubleshooting support in session
  • Loading branch information
tikhonp committed May 17, 2022
1 parent ae76497 commit ad98d91
Show file tree
Hide file tree
Showing 11 changed files with 113 additions and 23 deletions.
File renamed without changes.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,6 @@ dmypy.json
*.swp
.idea/
bash_profile
/YANDEX_PRIVATE_KEY.txt
/.YANDEX_PRIVATE_KEY.txt
/src/tests/test_rec.wav
/src/tests/test_synth.wav
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ env:
- secure: BVqjB2949yyVbhPZtjsH+FEEtKNMx5cRjaMbVSMO76El4vAP29iWy8V/L77Z1AqopoVs/MLpDxFS3K3aoruAWablhO2IJkUuxtOk01llm24ungy7GX6xthbHhquftdLtcGdtA+uf+TV1VAGf0mt0lMPxwvSJcYHP2lcPbwL9Oc3WVChLmKEGPWxu9MjHm0ZsnuFySvEYCXJKVfMj0daCfLBYo1S8KQ+C7wPUmk3XS3Dj3fzO/D3wH0Wrd7fxPeqhMD0cAh2xK8jEcoUo3DAbsjLG/vZ3zRQ8yVL20LZQWf0pwVloumenJ2y9bADWEdLUxOWs+SRdMSzFfpWXkD5sa8vWehUXnCleOaW2QTGse85Rckld1Iy7PW1yUsvgBRoz3W8DoE+rXpiLswrKd3ou86Lyk2A5kyN0ZAieP229TdLbAqxagZGzKzjNwqju9UUwF6Q6htUc4ZTyhgiVSdrmcpFcVip4Sh+EFDJlMtDC7bzvNfE8Ks4DFVJO38soYZn10eoG+1f/DFGeFvJxQTdzK7cZTRw5zHZ63NBpc1ycTcAHLgT+tewUtbQkh9VQ4x4QMOIC8Ms5MWupgfUaWyHQnLjwzZifgNgbdpMBeuQzcaDR8bstHWWqd1xRCzV3OcZeucbc7G4oAcZrMb7jiNxILX14fPxmeFdgCtb3/HmpXas=
- secure: P99UwLtmBMMyf2uYtTYqrliB0nNllQ+mZF5rHWdWnsml8gBmgbrkqbj3hPdwJCGTMvO0XgrFvSIEp1grtjbEdmYp/Yt0b6sspKLe9e2zGRET9NgZAZfkJ212MxMuVcStcrP5H+JByt766xGvUXI+xpoIB1VnYKcC3sq4GLtvcgQUMH3AQAcv3Py3t2CWFESzTqsKuKO8W1Z3bX4zpqQH5b1VL6B2nnNjQAUDU45Iz5MFqk4HgYJ+qGgJQMeBFsDrdtIiXzELLP2mrGopmRIehcDGRJh82QIoyhnBYDL8ynx9D+nuZnvqtJp1nqy9aQhyzonmHstHK5u1J5BgkxyT/0wImk2b1Go9TkVYeh2T9j/qmxpZlhylx1lCwXjLlNlAgCls7Hu4tYiwxrbUTR6Y8ak45S7hwDgoJUaZMUFtN4SrV/kRJoYa11ozcgFRp26q/1M6W+kNm0kmGT/Mm8eEjLSc01xWe34gMQawXB1CdGeL0GYZTj2VHYr0D98fiVKFkNobFsoX+NkKR0acAixRdaf5uGnPSyU0dRwuG9foPsW9qtbsyNHsuerbKaoudpXIISPUAVtRVgGITh/dpkiwflpojtjLCJQix7ajvKkcPjATZ6gR0BR6qDa9gyX71pAjUXqOIhodBXVD+M+HZjrwh4v3JcC2SeBkQ9AyidsZhAc=
- secure: UPF3CixbZ4FwRve+gdnfL9HtXTQ0MpwFOMU9O4TNWWiKjLB1TjFCDk7ma9Y5V7prEt86ku+EZtiefYENaMhR2V7VQredb/WtQhm7T1XfZt35zK0Pgf4zl60SYRdlVY6xTcz1TfFuGqJrAQmSEgnzHhP1zdOpx3npoT3kg5wGr1cMr5L6ugiBMapdAnaxHq3OxRKcspCkjIIKtjGLPNEGu+MkCUg9xbNey45zYRZCgNy5s265FGU3pgmAwkttYMmOvvdhVtvQFJfDHwQ8uG0dObhbouHv+Rcd0t4C4WwsO9I7VgHW3KrXyQZjTNa6qV0IRHdbj+KaDDqHnDtU5qqCKm1MujtS7nKNnXSfwmpA2Fz+lt1J3mCFKWhpzJs9A2xPC6d1K7DyRGiJ03NOYeWN67ET7eAHxPZkkEhEOZls45tPRTO9Xe8+BTel27ZfREgiHuqYy1tHhVO6J+/0yPKcLKARwYUh3+1/3Yjpk7dqzyMJ69ZP/plTFxNecDcwhQQOI8hBNI2AsStZABXP9D2vHOhkE0nVL9DKCmFBFetLVKXgLJ6Ih08LdNQ9mW4JRVIxr0tzMM1UquK6/OtQrWr/ZpGQ4bzjyoBYmsvLzWmdcvWfo/T0NBPWVnGiRTdHjDI3s3/954G8bPTMeWw8JNIjwPj67xirS51BaZQA8BOOW98=
- secure: j/FXFu+r+6vEnY9hXMnmxjGBMrEuHpE+YH0OdQSXVk+ZJeP35aVoD5f5xe8lK1mHeZF0g8in9KFwhEoXTP0bPAnIXXgzEN3Tk2Z08RoBvmB2Rf5bMWVoSuGxCv9i38hgvvY9Wl4lbjR+7XMAh3OZeiBtVDZEFbc2HpPvI4fGzhSKQ9mGHo/GB0j/C5d5qBKU/H4WLB6Po8u6Ws+XdlWnzq87zxi9QVm7ESX8/eEsWNVHONGV2hVTdYOEjUAltkNhIvlGCHP52n8XqeVlMCHFvMdzxapl+Qqix2n7kHW5T2dLMJnWSLFM89WbloWF0UE08cL7ZXl/KHDJWk0fMD9hI8gpBEw3/KTbtiJA9xJ+o3oHMNh1D0wFCiJsN9VjFkl6TJOvsz8UCTiGdE06Y2kUoU2FYuuGFi3TkQz5zs9LniANrGKtW2EHrNH7r834cuPxJ/+H48lMwCLnUtHr43heF9OKhh7Mb5lIuqYwBOj3ORii6Iwl8+583Hqpj91hlcdZ8lXjzjYkK6NGj4cICGDgB64PYNR29tBOHVK41XlI4WZBSXqK5HAlOcK1TuDhJ086U9LHdT0a5mT+C0RVMOluJ6272ox1uijdv/a/2IqixdCF8szxuGhpapADQxpz4ITDqfU5abqjbjZCfnI+mZ7ercRpHxt03uAzmFjDPgC1MBI=
before_install:
- openssl aes-256-cbc -K $encrypted_8171c61e233b_key -iv $encrypted_8171c61e233b_iv
-in YANDEX_PRIVATE_KEY.txt.enc -out YANDEX_PRIVATE_KEY.txt -d
-in .YANDEX_PRIVATE_KEY.txt.enc -out .YANDEX_PRIVATE_KEY.txt -d
- python --version
- pip install -U pip
- pip install -r requirements.txt
Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[metadata]
name = speechkit
version = 2.0.4
version = 2.1.0
author = Tikhon Petrishchev
author_email = [email protected]
description = Python SDK for Yandex Speechkit API.
Expand Down
2 changes: 1 addition & 1 deletion src/speechkit/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"""

__author__ = 'Tikhon Petrishchev'
__version__ = '2.0.4'
__version__ = '2.1.0'

from speechkit._auth import Session
from speechkit._recognition.streaming_recognition import DataStreamingRecognition
Expand Down
86 changes: 72 additions & 14 deletions src/speechkit/_auth.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import functools
import time
import uuid

import jwt
import requests
Expand Down Expand Up @@ -116,14 +116,23 @@ class Session:
API_KEY = 'api_key'
"""Api key if api-key auth, value: 'api_key'"""

def __init__(self, auth_type, credential, folder_id):
def __init__(self, auth_type, credential, folder_id, x_client_request_id_header=False,
x_data_logging_enabled=False):
"""
Stores credentials for given auth method
:param string auth_type: Type of auth may be :py:meth:`Session.IAM_TOKEN` or :py:meth:`Session.API_KEY`
:param string | None folder_id: Id of the folder that you have access to. Don't specify this field if
you make a request on behalf of a service account.
:param string credential: Auth key iam or api key
:param boolean x_client_request_id_header: include x-client-request-id. `x-client-request-id` is a unique
request ID. It is generated using uuid. Send this ID to the technical support team to help us find
a specific request in the system and assist you. To get x_client_request_id_header use
`Session.get_x_client_request_id()` method.
:param boolean x_data_logging_enabled: A flag that allows data passed by the user in the request to be saved.
By default, we do not save any audio or text that you send. If you pass the true value in this header,
your data is saved. This data, along with the request ID, will help the Yandex technical support team solve your
problem.
"""
if auth_type not in (self.IAM_TOKEN, self.API_KEY):
raise ValueError(
Expand All @@ -138,14 +147,25 @@ def __init__(self, auth_type, credential, folder_id):
self._credential = credential
self.folder_id = folder_id

self._x_client_request_id = str(uuid.uuid4()) if x_client_request_id_header else None
self._x_data_logging_enabled = x_data_logging_enabled

@classmethod
def from_api_key(cls, api_key, folder_id=None):
def from_api_key(cls, api_key, folder_id=None, x_client_request_id_header=False, x_data_logging_enabled=False):
"""
Creates session from api key
:param string api_key: Yandex Cloud Api-Key
:param string | None folder_id: Id of the folder that you have access to. Don't specify this field if
you make a request on behalf of a service account.
:param boolean x_client_request_id_header: include x-client-request-id. `x-client-request-id` is a unique
request ID. It is generated using uuid. Send this ID to the technical support team to help us find
a specific request in the system and assist you. To get x_client_request_id_header use
`Session.get_x_client_request_id()` method.
:param boolean x_data_logging_enabled: A flag that allows data passed by the user in the request to be saved.
By default, we do not save any audio or text that you send. If you pass the true value in this header,
your data is saved. This data, along with the request ID, will help the Yandex technical support team solve your
problem.
:return: Session instance
:rtype: Session
"""
Expand All @@ -160,16 +180,26 @@ def from_api_key(cls, api_key, folder_id=None):
if len(folder_id) == 0:
raise ValueError("folder_id must not be empty.")

return cls(cls.API_KEY, api_key, folder_id=folder_id)
return cls(cls.API_KEY, api_key, folder_id=folder_id, x_client_request_id_header=x_client_request_id_header,
x_data_logging_enabled=x_data_logging_enabled)

@classmethod
def from_yandex_passport_oauth_token(cls, yandex_passport_oauth_token, folder_id):
def from_yandex_passport_oauth_token(cls, yandex_passport_oauth_token, folder_id, x_client_request_id_header=False,
x_data_logging_enabled=False):
"""
Creates Session from oauth token Yandex account
:param string yandex_passport_oauth_token: OAuth token from Yandex.OAuth
:param string folder_id: Id of the folder that you have access to. Don't specify this field if
you make a request on behalf of a service account.
:param boolean x_client_request_id_header: include x-client-request-id. `x-client-request-id` is a unique
request ID. It is generated using uuid. Send this ID to the technical support team to help us find
a specific request in the system and assist you. To get x_client_request_id_header use
`Session.get_x_client_request_id()` method.
:param boolean x_data_logging_enabled: A flag that allows data passed by the user in the request to be saved.
By default, we do not save any audio or text that you send. If you pass the true value in this header,
your data is saved. This data, along with the request ID, will help the Yandex technical support team solve your
problem.
:return: Session instance
:rtype: Session
"""
Expand All @@ -187,16 +217,25 @@ def from_yandex_passport_oauth_token(cls, yandex_passport_oauth_token, folder_id

iam_token = get_iam_token(yandex_passport_oauth_token=yandex_passport_oauth_token)

return cls(cls.IAM_TOKEN, iam_token, folder_id=folder_id)
return cls(cls.IAM_TOKEN, iam_token, folder_id=folder_id, x_client_request_id_header=x_client_request_id_header,
x_data_logging_enabled=x_data_logging_enabled)

@classmethod
def from_jwt(cls, jwt_token, folder_id=None):
def from_jwt(cls, jwt_token, folder_id=None, x_client_request_id_header=False, x_data_logging_enabled=False):
"""
Creates Session from JWT token
:param string jwt_token: JWT
:param string | None folder_id: Id of the folder that you have access to. Don't specify this field if
you make a request on behalf of a service account.
:param boolean x_client_request_id_header: include x-client-request-id. `x-client-request-id` is a unique
request ID. It is generated using uuid. Send this ID to the technical support team to help us find
a specific request in the system and assist you. To get x_client_request_id_header use
`Session.get_x_client_request_id()` method.
:param boolean x_data_logging_enabled: A flag that allows data passed by the user in the request to be saved.
By default, we do not save any audio or text that you send. If you pass the true value in this header,
your data is saved. This data, along with the request ID, will help the Yandex technical support team solve your
problem.
:return: Session instance
:rtype: Session
"""
Expand All @@ -213,7 +252,8 @@ def from_jwt(cls, jwt_token, folder_id=None):

iam_token = get_iam_token(jwt_token=jwt_token)

return cls(cls.IAM_TOKEN, iam_token, folder_id=folder_id)
return cls(cls.IAM_TOKEN, iam_token, folder_id=folder_id, x_client_request_id_header=x_client_request_id_header,
x_data_logging_enabled=x_data_logging_enabled)

@property
def header(self):
Expand All @@ -224,9 +264,16 @@ def header(self):
:rtype: dict
"""
if self._auth_method == self.IAM_TOKEN:
return {'Authorization': 'Bearer {iam}'.format(iam=self._credential)}
if self._auth_method == self.API_KEY:
return {'Authorization': 'Api-Key {api_key}'.format(api_key=self._credential)}
h = {'Authorization': 'Bearer {iam}'.format(iam=self._credential)}
elif self._auth_method == self.API_KEY:
h = {'Authorization': 'Api-Key {api_key}'.format(api_key=self._credential)}
else:
return
if self._x_client_request_id is not None:
h.update({'x-client-request-id': self._x_client_request_id})
if self._x_data_logging_enabled:
h.update({'x-data-logging-enabled': 'true'})
return h

@property
def streaming_recognition_header(self):
Expand All @@ -238,10 +285,21 @@ def streaming_recognition_header(self):
"""

if self._auth_method == self.IAM_TOKEN:
return tuple(('authorization', 'Bearer {iam}'.format(iam=self._credential),))
if self._auth_method == self.API_KEY:
return tuple(('authorization', 'Api-Key {api_key}'.format(api_key=self._credential),))
h = tuple(('authorization', 'Bearer {iam}'.format(iam=self._credential),))
elif self._auth_method == self.API_KEY:
h = tuple(('authorization', 'Api-Key {api_key}'.format(api_key=self._credential),))
else:
return

if self._x_client_request_id is not None:
h = h + tuple(('x-client-request-id', self._x_client_request_id,))
if self._x_data_logging_enabled:
h = h + tuple(('x-data-logging-enabled', 'true',))
return h

@property
def auth_method(self):
return self._auth_method

def get_x_client_request_id(self):
return self._x_client_request_id
25 changes: 23 additions & 2 deletions src/tests/test_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@


def get_private_key():
with open('../YANDEX_PRIVATE_KEY.txt', 'rb') as f:
with open('../.YANDEX_PRIVATE_KEY.txt', 'rb') as f:
return f.read()


Expand All @@ -19,13 +19,23 @@ def test_generating(self):
jwt = generate_jwt(service_account_id, key_id, private_key)
self.assertIsInstance(jwt, str)

def test_assert_incorrect_service_account_id_key_id_private_key(self):
with self.assertRaises(ValueError):
generate_jwt('', 's', b'')

with self.assertRaises(ValueError):
generate_jwt('s', '', b'')

with self.assertRaises(ValueError):
generate_jwt('s', '', '')


class GetIamTokenTestCase(unittest.TestCase):
def test_assert_empty_data(self):
with self.assertRaises(ValueError):
get_iam_token()

def test_assert_invalid_data(self):
def test_assert_invalid_yandex_passport_oauth_token(self):
with self.assertRaises(ValueError):
get_iam_token(yandex_passport_oauth_token='', jwt_token='')

Expand Down Expand Up @@ -53,6 +63,10 @@ def test_request(self):
data = get_api_key(yandex_passport_oauth_token, service_account_id)
self.assertIsInstance(data, str)

def test_invalid_data_empty(self):
with self.assertRaises(ValueError):
get_api_key()


class SessionTestCase(TestCase):
def test_from_api_key(self):
Expand Down Expand Up @@ -87,6 +101,13 @@ def test_header_iam_token(self):
session = Session(Session.IAM_TOKEN, 'hello', None)
self.assertEqual(session.header, {'Authorization': 'Bearer hello'})

def test_auth_method(self):
session = Session(Session.API_KEY, 'hello', None)
self.assertEqual(session.auth_method, Session.API_KEY)

session = Session(Session.IAM_TOKEN, 'hello', None)
self.assertEqual(session.auth_method, Session.IAM_TOKEN)


if __name__ == '__main__':
unittest.main()
2 changes: 1 addition & 1 deletion src/tests/test_long_audio_recognition.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@


def get_private_key():
with open('../YANDEX_PRIVATE_KEY.txt', 'rb') as f:
with open('../.YANDEX_PRIVATE_KEY.txt', 'rb') as f:
return f.read()


Expand Down
2 changes: 1 addition & 1 deletion src/tests/test_short_audio_recognition.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@


def get_private_key():
with open('../YANDEX_PRIVATE_KEY.txt', 'rb') as f:
with open('../.YANDEX_PRIVATE_KEY.txt', 'rb') as f:
return f.read()


Expand Down
2 changes: 1 addition & 1 deletion src/tests/test_streaming_recognition.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@


def get_private_key():
with open('../YANDEX_PRIVATE_KEY.txt', 'rb') as f:
with open('../.YANDEX_PRIVATE_KEY.txt', 'rb') as f:
return f.read()


Expand Down
10 changes: 10 additions & 0 deletions src/tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,13 @@ def test_request(self):

data = list_of_service_accounts(session)
self.assertIsInstance(data, list)

def invalid_session_from_api_key(self):
session = Session.from_api_key('api_key', 'sdsas')
with self.assertRaises(ValueError):
list_of_service_accounts(session)

def invalid_session_no_folder_id(self):
session = Session.from_yandex_passport_oauth_token(api_key)
with self.assertRaises(ValueError):
list_of_service_accounts(session)

0 comments on commit ad98d91

Please sign in to comment.