Пример #1
0
class Config(object):
    logger.debug("Start of the Config() class.")

    # Apply the environment variables when running locally
    # When running in GCP, these are loaded from the env_variables.yaml file when the app loads
    if local:
        from env_tools import apply_env
        apply_env()
        logger.info("Local .env variables applied.")
        DEBUG = True
    else:
        DEBUG = False

    # Load AWS credentials
    AWS_ACCOUNT_ID = environ.get('AWS_ACCOUNT_ID')
    AWS_ACCESS_KEY = environ.get('AWS_ACCESS_KEY_ID')
    AWS_SECRET_ACCESS_KEY = environ.get('AWS_SECRET_ACCESS_KEY')
    AWS_USER = environ.get('AWS_USER')
    AWS_REGION = environ.get('AWS_REGION')

    # Load app-related credentials
    BOUND_PORT = 5000
    DOMAIN_URL = environ.get('DOMAIN_URL')
    WHITELISTED_ORIGIN = environ.get('WHITELISTED_ORIGIN')
    WHITELISTED_ORIGINS = environ.get('WHITELISTED_ORIGINS')
    SECRET_KEY = environ.get(
        'SECRET_KEY') or '0y4TJIyEjH8ZVkXPMGBiFEcHk8tdfe57kE1IJhvR1yb1cmWY'

    if SECRET_KEY != environ.get('SECRET_KEY'):
        logger.warning(
            "Error loading SECRET_KEY!  Temporarily using a hard-coded key.")

    logger.debug("End of the Config() class.")
Пример #2
0
    def test_logging(self):
        string_test = "Basic string"
        with LogCapture() as log:
            log.install()
            logger.debug("Debug-level test")
            logger.error("Error-level test")
            logger.warning("Warning-level test")
            logger.info("Info-level test")
            logger.info(f"f-string test for {string_test}")

            log.check_present(("backend.global_logger", "DEBUG", "Debug-level test"))
            log.check_present(("backend.global_logger", "ERROR", "Error-level test"))
            log.check_present(("backend.global_logger", "WARNING", "Warning-level test"))
            log.check_present(("backend.global_logger", "INFO", "Info-level test"))
            log.check_present(("backend.global_logger", "INFO", "f-string test for Basic string"))
Пример #3
0
    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        # Type check: last_modified
        # Accept `str` or an epoch (`float`, `int`) for last_modified
        # logger.debug(f"Type check for last_modified of picklist: {self.list_name}")
        # logger.debug(f"{type(self.last_modified)}, {self.last_modified}")
        # if !isinstance(self.last_modified, datetime):
        if isinstance(self.last_modified, (float, int)):
            self.last_modified = datetime.utcfromtimestamp(
                kwargs['last_modified'] / 1000)
        else:
            # Assume a string was provided and parse a datetime object from that
            try:
                self.last_modified = datetime.fromisoformat(
                    str(self.last_modified))
            except (TypeError, ValueError) as e:
                logger.debug(
                    f"Provided value for last_modified: {self.last_modified}, "
                    f"{type(self.last_modified)} cannot be converted to datetime."
                )
                raise ValueError(
                    f"Value for last_modified must be an epoch (float/int) or "
                    f"an iso-formatted string. {e}")
Пример #4
0
    def get(self) -> json:
        """Return the list of picklist values for all fields."""
        logger.debug(f"Request: {request}")

        try:
            # Read from the database
            logger.debug("Retrieving all picklist values...")
            all_picklists = Picklist.scan()

            # Convert each record to a dictionary, compile into a list
            output = []
            for picklist in all_picklists:
                output.append(picklist.to_dict())
                # picklist.save()
                # output = picklist.to_dict()
                # pass

            logger.debug(f"End of PicklistApi.GET")
            return {'message': 'Success', 'data': output}, 200

        except PynamoDBException as e:
            error_msg = f"Error attempting to retrieve picklists from the database."
            logger.debug(f"{error_msg}\n{e}")
            return {'message': 'Error', 'data': error_msg}, 500
Пример #5
0
    def get(self, beverage_id, location) -> json:
        """Return the specified beverage."""
        logger.debug(
            f"Request: {request}, for id: {beverage_id}, loc: {location}.")

        # Retrieve specified beverage from the database
        try:
            beverage = Beverage.get(beverage_id, location)
            logger.debug(f"Retrieved beverage: {beverage}")
            return {
                'message': 'Success',
                'data': beverage.to_dict(dates_as_epoch=True)
            }, 200

        except Beverage.DoesNotExist:
            logger.debug(f"Beverage {beverage_id} not found.")
            return {
                'message': 'Not Found',
                'data': f'Beverage {beverage_id} not found.'
            }, 404
        except PynamoDBException as e:
            error_msg = f"Error attempting to retrieve beverage {beverage_id}."
            logger.debug(f"{error_msg}\n{e}")
            return {'message': 'Error', 'data': error_msg}, 500
Пример #6
0
    def get(self) -> json:
        """Return all beverages in the database."""
        logger.debug(f"Request: {request}")

        try:
            # Read from the database
            beverages = Beverage.scan()

            # Convert each record to a dictionary, compile into a list
            output = []
            count = 0
            for bev in beverages:
                output.append(bev.to_dict(dates_as_epoch=True))
                count += 1

            logger.debug(f"End of CellarCollectionApi.GET")
            return {'message': 'Success', 'data': output}, 200

        except PynamoDBException as e:
            error_msg = f"Error attempting to retrieve beverages from the database."
            logger.debug(f"{error_msg}\n{e}")
            return {'message': 'Error', 'data': error_msg}, 500
skipped = 0
count = 0
success = 0
errors = []
beerlist = []

# Create table if necessary
if not Beverage.exists():
    logger.info(f"Creating a new table: {Beverage.Meta.table_name}.")
    Beverage.create_table(wait=True,
                          read_capacity_units=5,
                          write_capacity_units=5)
    logger.info(f"Table created.")

for item in example_data:
    logger.debug(f"Initializing a new Beverage for: {item}")
    beer = Beverage(**item)
    beerlist.append(beer)
    # logger.debug("Beverage initialized.")

logger.info(f"Starting batch save for {len(beerlist)} beers.")
print(f"Starting batch save for {len(beerlist)} beers.")
for beer in beerlist:
    logger.debug(f"Saving beer #{count+1}: {beer}.")
    print(f"Saving beer #{count+1}: {beer.to_dict()}")
    beer.save()

    logger.debug("Success!")
    count += 1

    if not local and count % 20 == 0:
Пример #8
0
    def put(self) -> json:
        """Add/update the list of picklist values."""
        logger.debug(f"Request: {request}")

        # Ensure there's a body to accompany this request
        if not request.data:
            return {'message': 'Error', 'data': 'PUT request must contain a body.'}, 400

        # Load & decode the provided JSON
        try:
            data = json.loads(request.data.decode())
            logger.debug(f"Data submitted: {data}")

        except json.JSONDecodeError as e:
            error_msg = f"Error attempting to decode the provided JSON."
            logger.debug(f"{error_msg},\n{request.data.__str__()},\n{e}")
            return {'message': 'Error', 'data': error_msg + f"\n{request.data.__str__()}"}, 400
        except BaseException as e:
            error_msg = f"Unknown error attempting to parse the provided JSON.\n{e}"
            logger.debug(error_msg)
            return {'message': 'Error', 'data': error_msg}, 400

        # Create a Picklist instance from the provided data
        try:
            picklist = Picklist(**data)
            picklist.last_modified = datetime.utcnow()
            logger.debug(f"Picklist instance created: {picklist}")

        except PynamoDBException as e:
            error_msg = f"Error parsing data into the Picklist model."
            logger.debug(f"{error_msg}\n{e}")
            return {'message': 'Error', 'data': f'{error_msg}\n{e}'}, 500

        # Save to the database
        try:
            logger.debug(f"Saving {picklist} to the db...")
            picklist.save()
            logger.info(f"Picklist updated: {picklist.list_name})")
            logger.debug(f"End of PicklistApi.PUT")
            return {'message': 'Success', 'data': picklist.to_dict()}, 200
        except Picklist.DoesNotExist:
            logger.debug(f"Picklist {picklist} not found.")
            return {'message': 'Not Found', 'data': f'Picklist {picklist} not found.'}, 404
        except PynamoDBException as e:
            error_msg = f"Error attempting to save picklist {picklist}."
            logger.debug(f"{error_msg}\n{e}")
            return {'message': 'Error', 'data': error_msg}, 500
Пример #9
0
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        # logger.debug(f"Initializing a new instance of the Beverage model for {kwargs}.")
        # Replace empty strings with None
        # Construct the concatenated beverage_id when not provided:
        #  producer, beverage name, year, size, {bottle date or batch}.  Bottle date preferred.
        if 'beverage_id' not in kwargs.keys():
            # Need to create a beverage_id for this beverage
            self.beverage_id = f"{kwargs['producer']}_{kwargs['name']}_{kwargs['year']}_{kwargs['size']}"
            if 'batch' in kwargs.keys() and 'bottle_date' in kwargs.keys():
                # If both bottle_date and batch are provided, prefer bottle_date
                self.beverage_id += f"_{kwargs['bottle_date']}"

            elif 'batch' not in kwargs.keys() or kwargs['batch'] == '':
                # Batch is not provided
                self.batch = None
                if 'bottle_date' not in kwargs.keys(
                ) or kwargs['bottle_date'] == '':
                    # When no batch or bottle_date is provided, append "_None"
                    self.beverage_id += "_None"
                    self.bottle_date = None
                else:
                    # Bottle_date is provided
                    self.beverage_id += f"_{kwargs['bottle_date']}"
            else:
                # Use batch when bottle_date isn't provided
                self.beverage_id += f"_{kwargs['batch']}"
            logger.debug(
                f"Created a beverage_id for this new Beverage: {self.beverage_id}."
            )

        # Must provide a location
        if 'location' not in kwargs.keys() or kwargs['location'] is None:
            logger.debug(f"No value for location provided, raising KeyError.")
            raise KeyError("Location is required.")

        # Type check: Year
        try:
            self.year = int(kwargs['year'])
        except ValueError as e:
            logger.debug(f"Year must be an integer.\n{e}")
            raise ValueError(f"Year must be an integer.\n{e}")

        # Type check: Batch
        if 'batch' in kwargs.keys():
            try:
                if self.batch and self.batch != "":
                    self.batch = int(kwargs['batch'])
                if self.batch == "":
                    self.batch = None
            except ValueError as e:
                logger.debug(f"Batch number must be an integer.\n{e}")
                raise ValueError(f"Batch number must be an integer.\n{e}")

        # Type check: qty
        if 'qty' in kwargs.keys():
            try:
                self.qty = int(kwargs['qty'])
            except ValueError as e:
                logger.debug(f"Qty must be an integer.\n{e}")
                raise ValueError(f"Qty must be an integer.\n{e}")

        # Type check: qty_cold
        if 'qty_cold' in kwargs.keys():
            try:
                self.qty_cold = int(kwargs['qty_cold'])
            except ValueError as e:
                logger.debug(f"Qty_cold must be an integer.\n{e}")
                raise ValueError(f"Qty_cold must be an integer.\n{e}")

        # Adjust last_modified due to JS working in milliseconds
        if 'last_modified' in kwargs.keys():
            # Accept an epoch (`float` or `int`) for date_added
            if isinstance(self.last_modified, (float, int)):
                self.last_modified = datetime.utcfromtimestamp(
                    kwargs['last_modified'] / 1000)
            else:
                # Assume a string was provided and parse a datetime object from that
                try:
                    self.last_modified = datetime.fromisoformat(
                        str(self.last_modified))
                except (TypeError, ValueError) as e:
                    logger.debug(
                        f"Provided value for last_modified: {self.last_modified}, "
                        f"{type(self.last_modified)} cannot be converted to datetime."
                    )
                    raise ValueError(
                        f"Value for last_modified must be an epoch (float/int) or "
                        f"an ISO-formatted string. {e}")
        else:
            self.last_modified = datetime.utcnow()

        # Type & value checks for date_added
        if 'date_added' in kwargs.keys():
            # Accept an epoch (`float` or `int`) for date_added
            if isinstance(self.date_added, (float, int)):
                self.date_added = datetime.utcfromtimestamp(
                    kwargs['date_added'] / 1000)
            else:
                # Assume a string was provided and parse a datetime object from that
                try:
                    self.date_added = datetime.fromisoformat(
                        str(self.date_added))
                except (TypeError, ValueError) as e:
                    logger.debug(
                        f"Provided value for date_added: {self.date_added}, "
                        f"{type(self.date_added)} cannot be converted to datetime."
                    )
                    raise ValueError(
                        f"Value for date_added must be an epoch (float/int) or "
                        f"an iso-formatted string. {e}")

            # Ensure date_added is always <= last_modified
            if self.date_added > self.last_modified:
                self.last_modified = self.date_added
Пример #10
0
    def post(self) -> json:
        """Add a new beverage to the database based on the provided JSON."""
        logger.debug(f"Request: {request}")

        # Ensure there's a body to accompany this request
        if not request.data:
            return {
                'message': 'Error',
                'data': 'POST request must contain a body.'
            }, 400

        # Load the provided JSON
        try:
            data = json.loads(request.data.decode())
            logger.debug(f"Data submitted: {type(data)}, {data}")
            # Replace empty strings with None
            for key in data.keys():
                if data[key] == "":
                    data[key] = None

            logger.debug(f"Data post-cleaning: {type(data)}, {data}")

        except json.JSONDecodeError as e:
            error_msg = f"Error attempting to decode the provided JSON."
            logger.debug(f"{error_msg},\n{request.data.__str__()},\n{e}")
            return {
                'message': 'Error',
                'data': error_msg + f"\n{request.data.__str__()}"
            }, 400
        except BaseException as e:
            error_msg = f"Unknown error attempting to parse the provided JSON.\n{e}"
            logger.debug(error_msg)
            return {'message': 'Error', 'data': error_msg}, 400

        # Create a new Beverage from the provided data
        try:
            new_beverage = Beverage(**data)
            logger.debug(f"New Beverage created: {new_beverage}")

        except PynamoDBException as e:
            error_msg = f"Error attempting to create new Beverage from: {data}."
            logger.debug(f"{error_msg}\nError: {e}.")
            return {'message': 'Error', 'data': error_msg}, 500
        except BaseException as e:
            error_msg = f"Unknown error creating a new Beverage from: {data}."
            logger.debug(f"{error_msg}\n{e}.")
            return {'message': 'Error', 'data': error_msg}, 500

        # Write this Beverage to the database
        try:
            logger.debug(
                f"Attempting to save Beverage {new_beverage} to the database.")
            new_beverage.save()
            logger.info(f"Successfully saved {new_beverage}.")
            logger.debug(f"End of CellarCollectionApi.POST")

            return {
                'message': 'Created',
                'data': new_beverage.to_dict(dates_as_epoch=True)
            }, 201
        except PynamoDBException as e:
            error_msg = f"Error attempting to save new beverage."
            logger.debug(f"{error_msg}\n{new_beverage}: {e}.")
            return {'message': 'Error', 'data': error_msg}, 500
Пример #11
0
    def delete(self, beverage_id, location) -> json:
        """Delete the specified beverage."""
        logger.debug(
            f"Request: {request}, for id: {beverage_id}, loc: {location}.")

        # Retrieve this beverage from the database
        try:
            beverage = Beverage.get(beverage_id, location)
            logger.debug(f"Retrieved beverage: {beverage}")

        except Beverage.DoesNotExist:
            logger.debug(f"Beverage {beverage_id} not found.")
            return {
                'message': 'Not Found',
                'data': f'Beverage {beverage_id} not found.'
            }, 404
        except PynamoDBException as e:
            error_msg = f"Error attempting to retrieve beverage {beverage_id}."
            logger.debug(f"{error_msg}\n{e}")
            return {'message': 'Error', 'data': error_msg}, 500

        # Delete the specified beverage
        try:
            footprint = f"{beverage}"
            beverage.delete()
            logger.info(f"Beverage {footprint} deleted successfully.")
            logger.debug("End of BeverageApi.DELETE")
            return {
                'message': 'Success',
                'data': f'{footprint} deleted successfully.'
            }, 200

        except PynamoDBException as e:
            error_msg = f"Error attempting to delete beverage: {beverage}."
            logger.debug(f"{error_msg}\n{e}")
            return {'message': 'Error', 'data': error_msg}, 500
Пример #12
0
    def put(self, beverage_id, location) -> json:
        """Update the specified beverage."""
        logger.debug(
            f"Request: {request}, for id: {beverage_id}, loc: {location}.")

        # Ensure there's a body to accompany this request
        if not request.data:
            return {
                'message': 'Error',
                'data': 'PUT request must contain a body.'
            }, 400

        # Load & decode the provided JSON
        try:
            data = json.loads(request.data.decode())
            logger.debug(f"Data submitted: {data}")

        except json.JSONDecodeError as e:
            error_msg = f"Error attempting to decode the provided JSON."
            logger.debug(f"{error_msg},\n{request.data.__str__()},\n{e}")
            return {
                'message': 'Error',
                'data': error_msg + f"\n{request.data.__str__()}"
            }, 400
        except BaseException as e:
            error_msg = f"Unknown error attempting to parse the provided JSON.\n{e}"
            logger.debug(error_msg)
            return {'message': 'Error', 'data': error_msg}, 400

        # Ensure the variables provided to the endpoint match the body details.
        if str(beverage_id) != str(data['beverage_id']):
            error_msg = f"/beverage_id provided to the endpoint ({beverage_id}) " \
                        f"doesn't match the beverage_id from the body ({data['beverage_id']})."
            logger.debug(f"{error_msg}")
            return {'message': 'Error', 'data': error_msg}, 400
        elif str(location) != str(data['location']):
            error_msg = f"/location provided to the endpoint ({location}) " \
                        f"doesn't match the location from the body ({data['location']})."
            logger.debug(f"{error_msg}")
            return {'message': 'Error', 'data': error_msg}, 400

        # Create a Beverage instance from the provided data
        try:
            beverage = Beverage(**data)
            beverage.last_modified = datetime.utcnow()
            logger.debug(f"Beverage instance created: {beverage}")

        except PynamoDBException as e:
            error_msg = f"Error parsing data into the Beverage model."
            logger.debug(f"{error_msg}\n{e}")
            return {'message': 'Error', 'data': f'{error_msg}\n{e}'}, 500

        # Save to the database
        try:
            logger.debug(f"Saving {beverage} to the db...")
            beverage.save()
            logger.info(f"Beverage updated: {beverage})")
            logger.debug(f"End of BeverageApi.PUT")
            return {
                'message': 'Success',
                'data': beverage.to_dict(dates_as_epoch=True)
            }, 200
        except Beverage.DoesNotExist:
            logger.debug(f"Beverage {beverage_id} not found.")
            return {
                'message': 'Not Found',
                'data': f'Beverage {beverage_id} not found.'
            }, 404
        except PynamoDBException as e:
            error_msg = f"Error attempting to save beverage {beverage_id}."
            logger.debug(f"{error_msg}\n{e}")
            return {'message': 'Error', 'data': error_msg}, 500