Beispiel #1
0
 def write_to_table(self, table: Table):
     """
     Write operations for this object need to be special cased (to avoid overwritting)
     Therefore we do not implement `to_dynamodb_item` however basically the body of that
     method is used in this class's impl of `write_to_table_if_not_found`
     """
     # put_item does not support UpdateExpression
     table.update_item(
         Key={
             "PK": self.get_dynamodb_content_key(self.content_id),
             "SK": self.get_dynamodb_content_type_key(),
         },
         # If ContentRef exists it needs to match or BAD THING(tm) can happen...
         ConditionExpression=Or(
             Attr("ContentRef").not_exists(),
             Attr("ContentRef").eq(self.content_ref)),  # type: ignore
         # Unfortunately while prod is happy with this on multiple lines pytest breaks...
         UpdateExpression=
         """SET ContentType = :ct, ContentRef = :cr, ContentRefType = :crt, SubmissionTimes = list_append(if_not_exists(SubmissionTimes, :empty_list), :s), CreatedAt = if_not_exists(CreatedAt, :c), UpdatedAt = :u ADD AdditionalFields :af""",
         ExpressionAttributeValues={
             ":ct": self.content_type.get_name(),
             ":cr": self.content_ref,
             ":crt": self.content_ref_type.value,
             ":af": self.additional_fields if self.additional_fields else
             {self.ADDITIONAL_FIELDS_PLACE_HOLDER},
             ":s": [s.isoformat() for s in self.submission_times],
             ":c": self.created_at.isoformat(),
             ":u": self.updated_at.isoformat(),
             ":empty_list": [],
         },
     )
Beispiel #2
0
    def save(self, force=False, save_if_missing=True, save_if_existing=True):
        _TRACE("DynamoObject.save reached")
        if not save_if_missing and not save_if_existing:
            raise RuntimeError(
                "At least one of save_if_missing and save_if_existing must be true."
            )

        old_version = DECIMAL(self.get(VERSION_KEY, -1))
        create_condition = Attr(VERSION_KEY).not_exists()
        if force:
            update_condition = Attr(VERSION_KEY).exists()
        else:
            update_condition = Attr(VERSION_KEY).eq(old_version)
        CE = None
        if force and save_if_missing and save_if_existing:
            pass
        elif save_if_missing and save_if_existing:
            CE = Or(create_condition, update_condition)
        elif save_if_existing:
            CE = update_condition
        else:
            # If we're here, we know that create_condition=True
            CE = create_condition
        try:
            self[VERSION_KEY] = old_version + 1
            if CE:
                self._store(CE)
            else:
                self._store()
            return self
        except ClientError as e:
            self[VERSION_KEY] = old_version
            raise e
Beispiel #3
0
    def _save(self, force=False, save_if_missing=True, save_if_existing=True, only_if_updated=False):
        if not save_if_missing and not save_if_existing:
            raise RuntimeError("At least one of save_if_missing and save_if_existing must be true.")

        if only_if_updated and not self._obj_updates:
            return self

        old_version = getattr(self, VERSION_KEY)
        create_condition = Attr(VERSION_KEY).not_exists()
        if force:
            update_condition = Attr(VERSION_KEY).exists()
        else:
            update_condition = Attr(VERSION_KEY).eq(old_version)
        CE = None
        if force and save_if_missing and save_if_existing:
            pass
        elif save_if_missing and save_if_existing:
            CE = Or(create_condition, update_condition)
        elif save_if_existing:
            CE = update_condition
        else:
            # If we're here, we know that create_condition=True
            CE = create_condition
        try:
            setattr(self, VERSION_KEY, old_version+1)
            if CE:
                self._store(CE)
            else:
                self._store()
            self._clear_update_record()
            self._in_db = True
            return self
        except ClientError as e:
            setattr(self, VERSION_KEY, old_version)
            raise e
def search_votes(search_form):
    keywords = search_form.vote_topic.data
    dynamodb = current_app.extensions['dynamo']

    keyword = re.sub(
        '  ', " ",
        re.sub(
            "[\u0060|\u0021-\u002c|\u002e-\u002f|\u003a-\u003f|\u2200-\u22ff|\uFB00-\uFFFD|\u2E80-\u33FF]",
            ' ', keywords)).split(' ')
    filter_expression_list = []

    while ("" in keyword):
        keyword.remove("")

    if len(keyword) > 1:
        for i in range(len(keyword)):
            filter_expression_list.append(
                Attr("topic").contains("{}".format(keyword[i])))

        expression = Or(*filter_expression_list)
    elif len(keyword) == 1:
        expression = Attr("topic").contains("{}".format(keyword[0]))
    else:
        return False

    results = dynamodb.tables['votes'].scan(FilterExpression=expression)
    return results["Items"]
Beispiel #5
0
 def test_or(self):
     cond1 = Equals(self.value, self.value2)
     cond2 = Equals(self.value, self.value2)
     or_cond = Or(cond1, cond2)
     self.build_and_assert_expression(
         or_cond,
         {'format': '({0} {operator} {1})',
          'operator': 'OR', 'values': (cond1, cond2)})
def test_update(mocker):
    mocker.patch.object(checkpoint, 'TABLE')
    checkpoint.update(5)
    checkpoint.TABLE.put_item.assert_called_with(Item={
        'id': checkpoint.RECORD_KEY,
        'since_id': 5
    },
                                                 ConditionExpression=Or(
                                                     Attr('id').not_exists(),
                                                     Attr('since_id').lt(5)))
Beispiel #7
0
def update(since_id):
    """Update checkpoint to given tweet id."""
    try:
        TABLE.put_item(Item={
            'id': RECORD_KEY,
            'since_id': since_id
        },
                       ConditionExpression=Or(
                           Attr('id').not_exists(),
                           Attr('since_id').lt(since_id)))
    except ClientError as e:
        if e.response['Error']['Code'] != 'ConditionalCheckFailedException':
            raise
def scan_schedule_data(prefix, weekday_ja):
    schedules_table_name = os.environ['SCHEDULES_TABLE_NAME']

    try:
        filter_exp = And(
            Attr('weekday').eq(weekday_ja),
            Or(Attr('prefix').eq(0),
               Attr('prefix').eq(prefix)))

        response = dynamodb.Table(schedules_table_name).scan(
            FilterExpression=filter_exp)

        return response['Items']
    except ClientError as e:
        print(e.response['Error']['Message'])
        return []
def lambda_handler(event, context):
    query_parameters = event["queryStringParameters"]
    tags = []
    [tags.append(value) for value in query_parameters.values()]
    if len(tags) == 1:
        filter_expression = Attr("tag").contains(tags[0])
    else:
        filter_expression = Or(*[(Attr("tag").contains(value))
                                 for value in tags])
    dynamodb = boto3.resource("dynamodb")
    table = dynamodb.Table("FIT5225Assignment2")
    response = table.scan(FilterExpression=filter_expression)
    result = {"links": []}
    [result["links"].append(item["link"]) for item in response["Items"]]
    return {
        "statusCode": 200,
        "body": json.dumps(result),
        "isBase64Encoded": "false"
    }
    def write_to_table(self, table: Table):
        """
        Write operations for this object need to be special cased (to avoid overwritting)
        Therefore we do not implement `to_dynamodb_item`

        If you're curious it would ~look like this:
        def to_dynamodb_item(self) -> dict:
            return {
                "PK": self.get_dynamodb_content_key(self.content_id),
                "SK": self.get_dynamodb_content_type_key(self.content_type),
                "ContentRef": self.content_ref,
                "ContentRefType": self.content_ref_type,
                "AdditionalFields": self.additional_fields,
                "SubmissionTimes": [s.isoformat() for s in self.submission_times],
                "CreatedOn": self.created_at.isoformat(),
                "UpdatedAt": self.updated_at.isoformat(),
            }
        """
        # put_item does not support UpdateExpression
        table.update_item(
            Key={
                "PK": self.get_dynamodb_content_key(self.content_id),
                "SK": self.get_dynamodb_content_type_key(self.content_type),
            },
            # If ContentRef exists it needs to match or BAD THING(tm) can happen...
            ConditionExpression=Or(
                Attr("ContentRef").not_exists(),
                Attr("ContentRef").eq(self.content_ref)),  # type: ignore
            # Unfortunately while prod is happy with this on multiple lines pytest breaks...
            UpdateExpression=
            """SET ContentRef = :cr, ContentRefType = :crt, SubmissionTimes = list_append(if_not_exists(SubmissionTimes, :empty_list), :s), CreatedAt = if_not_exists(CreatedAt, :c), UpdatedAt = :u ADD AdditionalFields :af""",
            ExpressionAttributeValues={
                ":cr": self.content_ref,
                ":crt": self.content_ref_type.value,
                ":af": self.additional_fields
                if self.additional_fields else {"Placeholder"},
                ":s": [s.isoformat() for s in self.submission_times],
                ":c": self.created_at.isoformat(),
                ":u": self.updated_at.isoformat(),
                ":empty_list": [],
            },
        )
Beispiel #11
0
def get_from_dynamo(name: str):
    dynamodb = boto3.resource("dynamodb", region_name='us-east-1')
    table_name = os.environ.get('tableName', None)
    all_responses = []

    if table_name is None:
        return all_responses

    table = dynamodb.Table(table_name)

    fe = Or(Attr('name').eq(name), Attr("location").eq(name))
    pe = "#g_id, #d_id, #name, #is_group, #delay, #loc"
    ean = {
        "#g_id": "group_id",
        "#d_id": "device_id",
        "#name": "name",
        "#is_group": "is_group",
        "#delay": 'delay',
        "#loc": "location"
    }

    response = table.scan(FilterExpression=fe,
                          ProjectionExpression=pe,
                          ExpressionAttributeNames=ean)

    for i in response['Items']:
        all_responses.append(i)

    while 'LastEvaluatedKey' in response:
        response = table.scan(ProjectionExpression=pe,
                              FilterExpression=fe,
                              ExpressionAttributeNames=ean,
                              ExclusiveStartKey=response['LastEvaluatedKey'])
        for i in response['Items']:
            all_responses.append(i)
    print(all_responses)
    return all_responses
Beispiel #12
0
 def test_or_operator(self):
     cond1 = Equals(self.value, self.value2)
     cond2 = Equals(self.value, self.value2)
     self.assertEqual(cond1 | cond2, Or(cond1, cond2))
def lambda_handler(event, context):
    """Sample pure Lambda function

    Parameters
    ----------
    event: dict, required
        API Gateway Lambda Proxy Input Format

        Event doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format

    context: object, required
        Lambda Context runtime methods and attributes

        Context doc: https://docs.aws.amazon.com/lambda/latest/dg/python-context-object.html

    Returns
    ------
    API Gateway Lambda Proxy Output Format: dict

        Return doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html
    """

    # try:
    #     ip = requests.get("http://checkip.amazonaws.com/")
    # except requests.RequestException as e:
    #     # Send some context about this error to Lambda Logs
    #     print(e)

    #     raise e
    print(event)
    method = event['httpMethod']
    print(f"method={method}")
    print(f"table_name={table_name}")
    myTriggerType = 'instrument_price'

    if method == "DELETE":
        #path=event['path']
        trigger_id = event['pathParameters']['trigger_id']
        print(f"triggerId={trigger_id}")

        try:
            #see https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/dynamodb.html#DynamoDB.Table.delete_item
            response = table.delete_item(
                Key={
                    'PK': f"TR#{myTriggerType}#{trigger_id}",
                    "SK": f"TR#{myTriggerType}#{trigger_id}"
                },
                ConditionExpression=And(
                    Attr('PK').eq(Attr('SK')),
                    Attr('triggerType').eq(myTriggerType)),
            )
        except ClientError as e:
            print(f"clientError={e}")
            if e.response['Error'][
                    'Code'] == 'ConditionalCheckFailedException':
                return iftttError(404, "item not found")
            raise
        print(f"response={response}")
        return {
            "statusCode": 200,
            "body": "",
        }

    elif method == "POST":
        body = json.loads(event['body'])
        trigger_id = body['trigger_identity']
        print(f"triggerId={trigger_id}")

        response = table.get_item(
            Key={
                'PK': f"TR#{myTriggerType}#{trigger_id}",
                "SK": f"TR#{myTriggerType}#{trigger_id}"
            },
            ProjectionExpression="triggerEvents, triggerType",
        )
        print(f"response={response}")

        if "Item" not in response:
            #brand new
            print(f"inserting {trigger_id}")
            if 'triggerFields' not in body:
                return iftttError(400, "triggerFields missing from request")
            triggerFields = body['triggerFields']
            #todo validate trigger fields
            try:
                response = table.put_item(
                    Item={
                        'PK': f"TR#{myTriggerType}#{trigger_id}",
                        "SK": f"TR#{myTriggerType}#{trigger_id}",
                        'triggerId': trigger_id,
                        #hacky string way to avoid having multiple columns
                        'triggerFields': json.dumps(triggerFields),
                        'triggerType': myTriggerType,
                    },
                    ConditionExpression=Or(
                        Attr('triggerType').eq(myTriggerType),
                        Attr('triggerType').not_exists()))
            except ClientError as e:
                print(f"clientError={e}")
                #somehow got created with someone elses triggerType
                if e.response['Error'][
                        'Code'] == 'ConditionalCheckFailedException':
                    return iftttError(404, "item not found")
                raise
            print("response ", response)
            triggered = []
        elif response['Item'].get("triggerType",
                                  myTriggerType) != myTriggerType:
            #it exists but it is someone elses
            return iftttError(404, "item not found")
        else:
            item = response['Item']
            print(f"found {item} ")
            #hacky string way to avoid having multiple columns
            #TODO: change this to use  a Map? (will allow to add without overwrite)
            events = json.loads(item.get("triggerEvents", "[]"))
            triggered = []
            for event in events:
                #TODO: implement limit (not needed now becasue I expect only up to one events)
                triggered.append(event['data'])

        return {
            "statusCode":
            200,
            "body":
            json.dumps({
                "data": triggered,
                # "location": ip.text.replace("\n", "")
            }),
        }
    else:
        return iftttError(400, f"unexpected httpMethod {method}")
Beispiel #14
0
 def test_or_operator(self):
     cond1 = Equals(self.value, self.value2)
     cond2 = Equals(self.value, self.value2)
     assert cond1 | cond2 == Or(cond1, cond2)
def lambda_handler(event, context):
    """Sample pure Lambda function

    Parameters
    ----------
    event: dict, required
        API Gateway Lambda Proxy Input Format

        Event doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format

    context: object, required
        Lambda Context runtime methods and attributes

        Context doc: https://docs.aws.amazon.com/lambda/latest/dg/python-context-object.html

    Returns
    ------
    API Gateway Lambda Proxy Output Format: dict

        Return doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html
    """

    # try:
    #     ip = requests.get("http://checkip.amazonaws.com/")
    # except requests.RequestException as e:
    #     # Send some context about this error to Lambda Logs
    #     print(e)

    #     raise e
    print(event)
    method=event['httpMethod']
    print(f"method={method}")
    print(f"table_name={table_name}")
    myTriggerType='prev_day_change' # TODO: get from path

    
    if method == "DELETE":
        #path=event['path']
        trigger_id=event['pathParameters']['trigger_id']
        print(f"triggerId={trigger_id}")

        try:
            #see https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/dynamodb.html#DynamoDB.Table.delete_item
            response = table.update_item(
                Key={'PK':f"TR#{myTriggerType}#{trigger_id}", "SK":f"TR#{myTriggerType}#{trigger_id}"},
                UpdateExpression="SET expiresOn = :val1",
                ExpressionAttributeValues={
                    ':val1': calendar.timegm(time.gmtime()),
                },
                ConditionExpression=And(
                    And(Attr('PK').exists(),Attr('expiresOn').not_exists()),
                    Attr('triggerType').eq(myTriggerType)),
            )
        except ClientError as e:
            print(f"clientError={e}")
            if e.response['Error']['Code']=='ConditionalCheckFailedException':
                return iftttError(404,"item not found")
            raise
        print(f"response={response}")
        return {
            "statusCode": 200,
            "body":"",
        }
        
    elif method == "POST":
        body=json.loads(event['body'])
        trigger_id=body['trigger_identity']
        limit = body.get('limit',50)
        print(f"triggerId={trigger_id}")
        
        ###########
        # for a PK TR#1 with events EV#1 EV#2 EV#N it will load:
        # TR#1,TR#1
        # TR#1,EV#N
        # TR#1,EV#N-1
        # ....
        # TR#1,EV#N-(limit-1)
        response = table.query(
            KeyConditionExpression=Key("PK").eq(f"TR#{myTriggerType}#{trigger_id}")
                #.__and__(Key("SK").begins_with(f"TR#{myTriggerType}#").__or__(Key("SK").begins_with(f"EV#")))
                #parked for now but how do I filter for keys begining with X or y? (probably with a query filter?)
                #TODO: filter query on SK, how do I do that?
                ,
            ScanIndexForward=False, #the latest X events + trigger (trigger sorts after events)
            Limit=limit + 1, #+1 for Trigger row
            ProjectionExpression="SK, triggerEvent, expiresOn",
        )
        #no need to itterate as we do not expect to filter out anything
        print(f"response={response}")
        items = response["Items"]
        if 0 == len(items) \
            or (not items[0]['SK'].startswith("TR#") )\
            or 'expiresOn' in items[0]:
            #brand new 
            print(f"inserting {trigger_id}")
            if 'triggerFields' not in body:
                return iftttError(400, "triggerFields missing from request")
            triggerFields=body['triggerFields']
            #todo validate trigger fields
            try:
                response = table.put_item(
                    Item={
                        'PK':f"TR#{myTriggerType}#{trigger_id}", 
                        "SK":f"TR#{myTriggerType}#{trigger_id}",
                        'triggerId': trigger_id,
                        #hacky string way to avoid having multiple columns
                        'triggerFields': json.dumps(triggerFields),
                        'triggerType': myTriggerType,
                    },
                    ConditionExpression=Or(
                        Attr('expiresOn').exists(),#previously deleted item # TODO: in this case we 'resurect' the old events. This should not happen
                        Attr('PK').not_exists() # brand new item
                    ),
                )
            except ClientError as e:
                print(f"clientError={e}")
                if e.response['Error']['Code']=='ConditionalCheckFailedException':
                    return iftttError(404,"item not found") # 
                raise
            print("response ",response)
            triggered=[]
        else:
            events = items[1:]
            print(f"found {events} ")
            #hacky string way to avoid having multiple columns
            #TODO: change this to use  a Map? (will allow to add without overwrite)
            #events = json.loads(item.get("triggerEvents","[]"))
            triggered= []
            now=calendar.timegm(time.gmtime())
            for event in events:
                if now< event.get('expiresOn',now+1):
                    triggered.append(json.loads(event['triggerEvent']))
                
        return {
            "statusCode": 200,
            "body": json.dumps({
                "data": triggered,
                # "location": ip.text.replace("\n", "")
            }),
        }
    else :
        return iftttError(400, f"unexpected httpMethod {method}")