Source code for accre.database.client.sapi_requests

"""
Mixin class for databse client with SAPI request related
functions
"""
import json
from datetime import datetime

from accre.database.model import *
from accre.database.util import limit_and_offset
from accre.exceptions import ACCREValueError


[docs]class DBClientSAPIRequestMixin: """ Functionality related to SAPI requests """
[docs] def add_sapi_request(self, *, action, payload, requester, ticket=0, status='PENDING', stage=0, creation_time=None, modification_time=None ): """ Create a new SAPI request with the specified attributes. :param str action: The SAPI action name for the request, i.e. NEWUSER, ADDGROUP, REMOVEGROUP, etc.. :param str payload: String representing a valid JSON object with properties for the action, the contents of the JSON are action dependent. :param str requester: The staff member or entity requesting the action. :param int ticket: The RT ticket number associated with this action. If set to 0 (default), a ticket will be generated. :param str status: The status code for the new request, defaults to pending :param int stage: Action-dependent processing stage code, default 0 :param datetime.datetime creation_time: Time when this request was created, if set to None it will be the current time :param datetime.datetime modification_time: Time of the last request modification, if set to None it will be the current time :returns: The SAPI request ID number of the new request :rtype: int """ if creation_time is None: creation_time = datetime.now() if modification_time is None: modification_time = datetime.now() # ensure that the payload is valid json, or simply let the # exception propagate up json.loads(payload) with self.engine.connect() as conn: ins = SAPI_REQUESTS.insert().values( action=action, payload=payload, ticket=ticket, requester=requester, status=status, stage=stage, creation_time=creation_time, modification_time=modification_time ) result = conn.execute(ins) return result.inserted_primary_key[0]
[docs] def list_sapi_requests(self, show_inactive=False, include_payloads=False, ticket=None ): """ Return a list of SAPI requests as dicts with the properties of each request. By default, inactive requests with the RESOLVED status are excluded and the json payload of each request is not included. :param bool show_inactive: Return all SAPI requests including those with status RESOLVED. :param bool include_payloads: Include the JSON payload data with each request. :param int ticket: If not None, return only requests with the specified RT ticket id :returns: List of dicts containing properties of each request :rtype: list(dict) """ if include_payloads: selections = [SAPI_REQUESTS] else: selections = [ SAPI_REQUESTS.c.id, SAPI_REQUESTS.c.action, SAPI_REQUESTS.c.ticket, SAPI_REQUESTS.c.requester, SAPI_REQUESTS.c.status, SAPI_REQUESTS.c.stage, SAPI_REQUESTS.c.creation_time, SAPI_REQUESTS.c.modification_time ] with self.engine.connect() as conn: if not show_inactive and ticket is not None: s = select(selections).where( (SAPI_REQUESTS.c.status != 'RESOLVED') & (SAPI_REQUESTS.c.ticket == ticket) ) elif not show_inactive and ticket is None: s = select(selections).where( SAPI_REQUESTS.c.status != 'RESOLVED' ) elif show_inactive and ticket is not None: s = select(selections).where( SAPI_REQUESTS.c.ticket == ticket ) else: s = select(selections) requests = conn.execute(s).fetchall() result = [dict(r) for r in requests] if include_payloads: for req in result: req['payload'] = json.loads(req['payload']) result.sort(key=lambda x: x['id']) return result
[docs] def sapi_request_info(self, srid): """ Return all information about a specified SAPI request identified by numeric ID. :param int srid: Numeric ID of the SAPI Request :returns: All data for the specified SAPI Request :rtype: dict """ with self.engine.connect() as conn: s = select([SAPI_REQUESTS]).where( srid == SAPI_REQUESTS.c.id ) request = conn.execute(s).fetchone() result = dict(request) result['payload'] = json.loads(result['payload']) return result
[docs] def all_sapi_requests_info( self, show_inactive=False, limit=None, offset=None ): """ Retrieve general information for all SAPI requests, optionally those that are already resolved :param bool show_inactive: Return all SAPI requests including those with status RESOLVED. :param int limit: Maximum number of records to return, return all if None :param int offset: Offset from first record to begin list of records, start at the first record if None :returns: General account information for all SAPI requests :rtype: list(dict) """ with self.engine.connect() as conn: s = select([SAPI_REQUESTS]) if not show_inactive: s = s.where(SAPI_REQUESTS.c.status != 'RESOLVED') request = conn.execute(s).fetchone() s = limit_and_offset(s, limit=limit, offset=offset) requests = conn.execute(s).fetchall() return [dict(a) for a in requests]
[docs] def modify_sapi_request(self, srid, **updates): """ Modify one or more fields of an existing SAPI request and update the modification time. :param int srid: Numeric ID of the SAPI request :param dict updates: Columns to be updated with their values note that payload should be a json-compatible dict and will be stringified, and modification_time will be added if not present """ if 'modification_time' not in updates: updates['modification_time'] = datetime.now() if 'payload' in updates: updates['payload'] = json.dumps(updates['payload']) invalid_fields = [ c for c in updates if c not in SAPI_REQUESTS.columns or c == 'id' ] if invalid_fields: raise ACCREValueError( 'Invalid account fields for update: {}'.format(invalid_fields) ) with self.engine.begin() as conn: up = SAPI_REQUESTS.update().values(**updates).where( SAPI_REQUESTS.c.id == srid ) conn.execute(up)
[docs] def delete_sapi_request(self, srid): """ Remove the specified SAPI request from the database. Note that under normal circumstances a SAPI request should be marked RESOLVED but not deleted, only use this method to remove spurious SAPI requests generated by testing procedures or automated errors. :param int srid: Numeric ID of the SAPI request """ with self.engine.begin() as conn: delete = SAPI_REQUESTS.delete().where( SAPI_REQUESTS.c.id == srid ) conn.execute(delete)