Source code for hmrc.cli.base

"""HMRC API command line"""

from argparse import Namespace
from configparser import ConfigParser, DEFAULTSECT, NoOptionError
from dataclasses import dataclass
import logging
import os.path
from typing import ClassVar, Set
import webbrowser
import parsedatetime
from ..auth import HmrcSession, HmrcTokenFileStorage
from ..api import HmrcClient

__all__ = [
    'datestring',
    'Command',
    'LoginCommand',
    'LogoutCommand',
]

calendar = parsedatetime.Calendar()


[docs]def datestring(string): """Parse date from string""" timestamp, ret = calendar.parseDT(string) if not ret: raise ValueError("Invalid date: '%s'" % string) return timestamp.date()
[docs]@dataclass class Command: """Command line command""" args: Namespace """Parsed arguments""" section: ClassVar[str] = DEFAULTSECT """Configuration file section""" Client: ClassVar[type] = HmrcClient """API client class"""
[docs] @classmethod def init_parser(cls, parser): """Initialise argument parser""" # Use class documentation as subcommand description parser.description = cls.__doc__ # Add common argument definitions parser.add_argument('--scenario', help="Use named test scenario") parser.add_argument('-d', '--debug', action='store_true', help="Enable debug logging") parser.add_argument('-c', '--config', help="Configuration file") parser.add_argument('--token', help="Authentication token file") parser.add_argument('--gdpr-consent', action='store_true', help="Consent to sending fraud prevention headers")
def __call__(self): # Extract configuration file parameters config = self.config section = self.section if section not in config: section = config.default_section # Extract session parameters try: client_id = config.get(section, 'client_id', fallback=None) client_secret = config.get(section, 'client_secret', fallback=None) server_token = config.get(section, 'server_token', fallback=None) test = config.getboolean(section, 'test', fallback=False) except NoOptionError as exc: raise SystemExit( "Missing configuration file option '%s'" % exc.option ) from exc # Construct client parameters params = {k: v for k, v in config[section].items() if k not in {'client_id', 'client_secret', 'server_token', 'test'}} # Enable debug logging, if applicable if self.args.debug: logging.basicConfig(level=logging.DEBUG) # Consent to GDPR data collection, if applicable gdpr_consent = self.args.gdpr_consent # Execute command with self.storage as storage: token = None if 'access_token' in storage.token else server_token with HmrcSession(client_id, client_secret=client_secret, storage=storage, token=token, test=test, gdpr_consent=gdpr_consent) as session: client = self.Client(session, **params) return self.execute(client)
[docs] def execute(self, client): """Execute command""" pass
@property def config(self): """Configuration""" config = ConfigParser() config.read([self.args.config] if self.args.config else ['hmrc.ini', os.path.expanduser('~/.hmrc.ini')]) return config @property def storage(self): """Token storage""" return HmrcTokenFileStorage(path=self.args.token)
[docs]class LoginCommand(Command): """Log in to HMRC APIs""" Clients: ClassVar[Set[type]] = {HmrcClient} """Set of all known client classes""" def __init_subclass__(cls, **kwargs): super().__init_subclass__(**kwargs) cls.Clients.add(cls.Client)
[docs] @classmethod def init_parser(cls, parser): super().init_parser(parser) parser.add_argument('--scope', action='append', help="Authorization scope") parser.add_argument('--code', help="Authorization code")
[docs] def execute(self, client): # Determine scope scope = (self.args.scope or client.scope or [x for Client in self.Clients for x in Client.scope]) # Apply scope to session session = client.session session.extend_scope(scope) # Obtain authorization code via browser, if applicable code = self.args.code if not code: uri, _state = session.authorization_url() webbrowser.open(uri) try: code = input("Enter authorization code from browser: ").strip() except KeyboardInterrupt as exc: raise SystemExit("Aborted") from exc # Obtain token session.fetch_token(code=code)
[docs]class LogoutCommand(Command): """Log out from HMRC APIs"""
[docs] def execute(self, client): client.session.storage.delete()