Coverage for src/artemis_sg/app_creds.py: 71%

1"""artemis_sg.app_creds

2

3 Obtains Google API Application Credentials."""

4

5 import os.path

6

7 from google.auth.exceptions import RefreshError

8 from google.auth.transport.requests import Request

9 from google.oauth2.credentials import Credentials

10 from google_auth_oauthlib.flow import InstalledAppFlow

11

12 from artemis_sg.config import CFG

13

14 app_token = CFG["google"]["docs"]["api_creds_token"]

15 google_credentials = CFG["google"]["docs"]["api_creds_file"]

16

17

18 def app_creds(app_token_filename: str = app_token) -> str:

19 """Obtain Google API application credentials.

20

21 Check given `app_token_filename` for valid credentials. If they don't

22 exist or are not valid, then use

23 `google_auth_oauthlib.flow.InstalledAppFlow` to let the user manually

24 approve the credential request. When successful, the credentials are saved

25 to the given `app_token_filename` and returned.

26

27 :param app_token_filename:

28 The filename used to inspect and save the credentials in (default is

29 app_creds_token.json). In practice this would not be set, but is given

30 here as a parameter for unittesting purposes.

31 :returns: a json string of the credentials

32 """

33

34 scopes = (

35 "https://www.googleapis.com/auth/spreadsheets.readonly",

36 "https://www.googleapis.com/auth/presentations",

37 )

38 creds = None

39 # The file `app_token_filename` stores the user's access and refresh tokens, and is

40 # created automatically when the authorization flow completes for the first

41 # time.

42

43 if os.path.exists(app_token_filename):

44 creds = Credentials.from_authorized_user_file(app_token_filename, scopes)

45 # If there are no (valid) credentials available, let the user log in.

46 if not creds or not creds.valid:

47 if creds and creds.expired and creds.refresh_token: 47 ↛ 48line 47 didn't jump to line 48, because the condition on line 47 was never true

48 try:

49 creds.refresh(Request())

50 except RefreshError:

51 flow = InstalledAppFlow.from_client_secrets_file(

52 google_credentials, scopes

53 )

54 creds = flow.run_local_server(port=0)

55 else:

56 flow = InstalledAppFlow.from_client_secrets_file(google_credentials, scopes)

57 creds = flow.run_local_server(port=0)

58 # Save the credentials for the next run

59 _write_token_file(app_token_filename, creds)

60

61 return creds

62

63

64 def _write_token_file(filename: str, creds: str):

65 with open(filename, "w", encoding="utf-8") as token:

66 token.write(creds.to_json())