Beispiel #1
0
 def init_app(self, local_dynamodb=None, prefix="", skip=False):
     name_template = prefix + "{table_name}"
     if local_dynamodb is not None:
         self._init_local(local_dynamodb, name_template)
     else:
         self.engine = Engine(table_name_template=name_template)
     self.engine.bind(self.Model, skip_table_setup=skip)
Beispiel #2
0
def engine(session, dynamodb, dynamodbstreams):
    # HACK: These clients won't be used.  We're going to replace the session immediately.
    engine = Engine(dynamodb=dynamodb, dynamodbstreams=dynamodbstreams)
    # Toss the clients above and hook up the mock session
    engine.session = session
    engine.bind(BaseModel)
    return engine
Beispiel #3
0
class DynamoDb:
    def __init__(self):
        self._local_dynamodb = None
        self.Model = BaseModel
        self.engine = None

    def _init_local(self,
                    local_dynamodb="http://127.0.0.1:4444",
                    name_template="{table_name}"):
        dynamodb = boto3.client("dynamodb", endpoint_url=local_dynamodb)
        dynamodbstreams = boto3.client("dynamodbstreams",
                                       endpoint_url=local_dynamodb)

        self.engine = Engine(dynamodb=dynamodb,
                             dynamodbstreams=dynamodbstreams,
                             table_name_template=name_template)

        client = patch_engine(self.engine)

        client.mock_ttl["MyTableName"] = True
        client.mock_backups["MyTableName"] = False

    def init_app(self, local_dynamodb=None, prefix="", skip=False):
        name_template = prefix + "{table_name}"
        if local_dynamodb is not None:
            self._init_local(local_dynamodb, name_template)
        else:
            self.engine = Engine(table_name_template=name_template)
        self.engine.bind(self.Model, skip_table_setup=skip)
Beispiel #4
0
    def _init_local(self,
                    local_dynamodb="http://127.0.0.1:4444",
                    name_template="{table_name}"):
        dynamodb = boto3.client("dynamodb", endpoint_url=local_dynamodb)
        dynamodbstreams = boto3.client("dynamodbstreams",
                                       endpoint_url=local_dynamodb)

        self.engine = Engine(dynamodb=dynamodb,
                             dynamodbstreams=dynamodbstreams,
                             table_name_template=name_template)

        client = patch_engine(self.engine)

        client.mock_ttl["MyTableName"] = True
        client.mock_backups["MyTableName"] = False
Beispiel #5
0
def engine_for_region(region, table_name_template="{table_name}"):
    dynamodb = boto3.client("dynamodb", region_name=region)
    dynamodbstreams = boto3.client("dynamodbstreams", region_name=region)
    return Engine(
        dynamodb=dynamodb,
        dynamodbstreams=dynamodbstreams,
        table_name_template=table_name_template
    )
Beispiel #6
0
def engine(dynamodb, dynamodbstreams, request):
    engine = Engine(dynamodb=dynamodb,
                    dynamodbstreams=dynamodbstreams,
                    table_name_template="{table_name}" +
                    request.config.getoption("--nonce"))
    yield engine

    # This collects all subclasses of BaseModel and are not abstract.  We are trying to delete any data in
    # dynamodb-local between unit tests so we don't step on each other's toes.
    concrete = set(
        filter(lambda m: not m.Meta.abstract, walk_subclasses(BaseModel)))
    for model in concrete:
        # we can run into a situation where the class was created, but not bound in the engine (or table created), so
        # we only try.  As the dynamodb-local process is only running in memory this isn't too much of a problem.
        try:
            objs = list(engine.scan(model))
            if objs:
                engine.delete(*objs)
        except BloopException:
            pass
Beispiel #7
0
def push(engine: Engine, id: str) -> Tuple[int, int]:
    """Returns (status, position) tuple.

    next_position should be a guess; always guess low, or misordering can occur.

    Position will be the reserved queue position, even if the user already existed."""

    # -1. Non-authoritative check for existing user.
    #     This doesn't guarantee the user doesn't exist, it just saves a lot of empty queue positions
    #     when a malicious actor tries to enqueue themselves many times.
    #     (Mitigation factor depends on )
    try:
        existing = engine.query(Person, key=Person.id == id).first()
        # If we don't hit an exception, person already exists
        return 409, existing.position
    except ConstraintViolation:
        # Probably doesn't exist, keep going
        pass

    # 0. Reserve a row in QueueEntry with the next queue position.
    #    Don't insert the id until we confirm that this user
    #    hasn't already been enqueued.
    entry = QueueEntry(position=0, enqueued_at=pendulum.now(), served_at=None)
    while True:
        try:
            engine.save(entry, condition=QueueEntry.NOT_EXIST)
        except ConstraintViolation:
            # TODO | can be much faster:
            # TODO | - use a binary walk from 0 until we hit a missing,
            # TODO | - then binary walk back to an existing,
            # TODO | - then use try save with increment of 1
            entry.position += 1
        else:
            break

    # 1. Now that we've reserved a row, try to build the id -> position mapping.
    #    Note that a failure here means the user is already queued, so we abort.
    #    Can't roll back the QueueEntry though, since another caller may have already
    #    advanced past us.
    person = Person(id=id, position=entry.position)
    try:
        engine.save(person, condition=Person.NOT_ENQUEUED)
    except ConstraintViolation:
        return 409, entry.position

    # 2. Success!  Finally, push the id into the QueueEntry.
    #    It's ok if this fails, because we can always rebuild the id from the Person table.
    entry.id = id
    try:
        engine.save(entry)
    except BloopException:
        # Can't be sure the entry is saved, but it's fine because we'll
        # be able to rebuild from the Person
        return 404, entry.position

    return 200, entry.position
Beispiel #8
0
def serve(engine: Engine, id: str) -> int:
    """200 success, 404 missing row, 409 already served"""
    # 0. Find the QueueEntry by Person
    person = Person(id=id)
    try:
        engine.load(person)
    except MissingObjects:
        return 404
    entry = QueueEntry(position=person.position)
    try:
        engine.load(entry)
    except MissingObjects:
        return 404

    # 1. Update the time the entry was served at.
    #    If the entry was already served, don't change the time.
    entry.served_at = pendulum.now()
    try:
        engine.save(entry, condition=QueueEntry.served_at.is_(None))
    except ConstraintViolation:
        # Already served, no change
        return 409
    else:
        return 200
Beispiel #9
0
def engine(dynamodb, dynamodbstreams):
    engine = Engine(dynamodb=dynamodb, dynamodbstreams=dynamodbstreams)
    yield engine
    engine.delete(*engine.scan(User))
Beispiel #10
0
    if len(args) >= 2:
        stage = args[1]

        config_filename = 'config.' + stage + '.json'
        parent_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
        config_filepath = os.path.join(parent_dir, config_filename)

        with open(config_filepath, 'r') as fp:
            config = json.load(fp)

        region = config['REGION']

        try:
            endpoint = args[2]
            client = boto3.client('dynamodb', region_name=region, endpoint_url=endpoint)
            engine = patch_engine(Engine(dynamodb=client))
        except IndexError:
            client = boto3.client('dynamodb', region_name=region)
            engine = Engine(dynamodb=client)

        resp = client.list_tables()
        tables = resp['TableNames']

        for model in models:
            model.Meta.table_name = model.Meta.table_name.format(STAGE=stage)

        print('Running dynamodb tables creation script '
              'in Region: {REGION}'.format(REGION=region))
        print("Dynamodb Tables: ")
        for model in models:
            if model.Meta.table_name not in tables:
        stage = args[1]

        os.environ['STAGE'] = stage

        config_filename = 'config.' + stage + '.json'
        parent_dir = os.path.dirname(
            os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
        config_filepath = os.path.join(parent_dir, config_filename)

        with open(config_filepath, 'r') as fp:
            config = json.load(fp)

        region = config['REGION']

        client = boto3.client('dynamodb', region_name=region)
        engine = Engine(dynamodb=client)

        model = Deployment

        # pull all items from deployment table
        table = model.Meta.table_name
        print("Updating brewoptix-deployment with new schema")

        print("Getting all items in table")
        all_items = client.scan(TableName=table)

        # delete existing table
        print("Delete table")
        resp = client.delete_table(TableName=table)

        for _ in range(10):
import json
from os import environ
from uuid import uuid4, UUID

import boto3
from bloop import Engine, ConstraintViolation

from todoapi.models.todo import TodoItem

if environ.get("AWS_SAM_LOCAL") == "true":
    dynamodb = boto3.client('dynamodb', endpoint_url="http://dynamodb:8000")
    db = Engine(dynamodb=dynamodb)
    db.bind(TodoItem)

else:
    dynamodb = boto3.client('dynamodb')
    db = Engine(dynamodb=dynamodb)
    db.bind(TodoItem, skip_table_setup=True
            )  # we can skip the table setup because CloudFormation will do it.


def get(event, context):
    if event['pathParameters']:
        todo_item = db.query(TodoItem,
                             key=TodoItem.uuid == UUID(
                                 event['pathParameters']['todo_id'])).one()
        body = todo_item.as_dict
    else:
        todos = db.scan(TodoItem)
        body = {"todos": [todo_item.as_dict for todo_item in todos]}
    return {"body": json.dumps(body), "statusCode": 200}
Beispiel #13
0
from . import aws
from . import exc
import uuid
from bloop import Engine, UUID, Boolean, Column, ConstraintViolation
engine = Engine(session=aws.session)


def default(kwargs, key, value):
    kwargs[key] = kwargs.get(key, value)


class Blob(engine.model):
    class Meta:
        table_name = "blobapy-blob"

    key_name = Column(UUID, hash_key=True, name='k')
    admin_key = Column(UUID, name='a')
    deleted = Column(Boolean, name='d')

    NOT_EXISTS = key_name.is_(None)

    def __init__(self, **kwargs):
        default(kwargs, "deleted", False)
        super().__init__(**kwargs)

    @classmethod
    def unique(cls):
        with exc.on_botocore("Failed to generate unique key_name"):
            retries = 5
            while retries:
                obj = cls(key_name=uuid.uuid4(), admin_key=uuid.uuid4())
Beispiel #14
0

class Tweet(BaseModel):
    class Meta:
        write_units = 10

    account = Column(UUID, hash_key=True)
    id = Column(String, range_key=True)
    content = Column(String)
    date = Column(DateTime)
    favorites = Column(Integer)

    by_date = GlobalSecondaryIndex(hash_key='date', projection='keys')


engine = Engine()
engine.bind(BaseModel)

# ================================================
# Usage
# ================================================

account = Account(id=uuid.uuid4(), name='@garybernhardt', email='REDACTED')
tweet = Tweet(account=account.id,
              id='616102582239399936',
              content='today, I wrote a type validator in Python, as you do',
              favorites=9,
              date=datetime.now(timezone.utc))

engine.save(account, tweet)
if __name__ == "__main__":
    if __name__ == '__main__':
        import sys
        import json
        import os

        args = sys.argv
        if len(args) >= 2:
            stage = args[1]

            config_filename = 'config.' + stage + '.json'
            parent_dir = os.path.dirname(
                os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
            config_filepath = os.path.join(parent_dir, config_filename)

            try:
                with open(config_filepath, 'r') as fp:
                    config = json.load(fp)
            except FileNotFoundError:
                print("Cannot find config file: {0}".format(config_filename))
                sys.exit(1)

            region = config['REGION']

            client = boto3.client('dynamodb', region_name=region)
            engine = Engine(dynamodb=client)

            print("Creating deployment table in Dynamodb: ")
            print('Creating table: ', Deployment.Meta.table_name)
            engine.bind(Deployment)
Beispiel #16
0
import arrow
import uuid
from bloop import (Engine, Column, Integer, DateTime, UUID,
                   GlobalSecondaryIndex, String, new_base)
engine = Engine()
Base = new_base()


class Account(Base):
    class Meta:
        read_units = 5
        write_units = 2

    id = Column(UUID, hash_key=True)
    name = Column(String)
    email = Column(String)
    by_email = GlobalSecondaryIndex(hash_key='email',
                                    projection='keys_only',
                                    write_units=1,
                                    read_units=5)


class Tweet(Base):
    class Meta:
        write_units = 10

    account = Column(UUID, hash_key=True)
    id = Column(String, range_key=True)
    content = Column(String)
    date = Column(DateTime)
    favorites = Column(Integer)
Beispiel #17
0
        'Rating': Number,
        'Updated': DateTime,
        'Description': Map(**{
            'Title': String,
            'Body': String
        }),
        'Sellers': Set(Integer)
    })


class Item(BaseModel):
    id = Column(UUID, hash_key=True)
    data = Column(Product)


engine = Engine()
engine.bind(BaseModel)

# ================================================
# Usage
# ================================================

item = Item(id=uuid.uuid4())
item.data = {
    'Name': 'item-name',
    'Rating': decimal.Decimal(str(random.random())),
    'Updated': datetime.now(timezone.utc),
    'Description': {
        'Title': 'item-title',
        'Body': 'item-body',
    },
Beispiel #18
0
import arrow
import boto3
import decimal
import random
import uuid
from bloop import (Engine, Column, DateTime, Integer,
                   UUID, String, Map, TypedMap, Float)


# ================================================
# Model setup
# ================================================

session = boto3.session.Session(profile_name="test-user-bloop")
engine = Engine(session=session)


Product = Map(**{
    'Name': String,
    'Rating': Float,
    'Updated': DateTime('US/Pacific'),
    'Description': Map(**{
        'Title': String,
        'Body': String
    }),
    'Sellers': TypedMap(Integer)
})


class Item(engine.model):
    id = Column(UUID, hash_key=True)
Beispiel #19
0
def bind_all(engine: Engine) -> None:
    engine.bind(Person)
    engine.bind(QueueEntry)
Beispiel #20
0
import arrow
import boto3
import uuid
from bloop import (Engine, Column, Integer, DateTime, UUID,
                   GlobalSecondaryIndex, String)

# ================================================
# Model setup
# ================================================

session = boto3.session.Session(profile_name="test-user-bloop")
engine = Engine(session=session)


class Account(engine.model):
    class Meta:
        read_units = 5
        write_units = 2

    id = Column(UUID, hash_key=True)
    name = Column(String)
    email = Column(String)
    by_email = GlobalSecondaryIndex(
        hash_key='email', projection='keys_only',
        write_units=1, read_units=5)


class Tweet(engine.model):
    class Meta:
        write_units = 10
    account = Column(UUID, hash_key=True)
Beispiel #21
0
from bloop import BaseModel, Column, String, Integer, Engine

from bloop.exceptions import ConstraintViolation


class NumberStore(BaseModel):
    key = Column(String, hash_key=True)
    value = Column(Integer)


engine = Engine(table_name_template="my-memory-{table_name}")
engine.bind(NumberStore)


def get(key):
    try:
        return engine.query(NumberStore,
                            key=NumberStore.key == key).one().value
    except ConstraintViolation:
        return None


def set(key, value):
    update = NumberStore(key=key, value=value)
    engine.save(update)
Beispiel #22
0

class Paste(SortByVersion, BaseModel):
    class Meta:
        ttl = {"column": "not_after"}

    not_after = Column(Timestamp, default=new_expiry)
    bucket = Column(String, dynamo_name="b")
    key = Column(String, dynamo_name="k")


class UserImage(SortByVersion, BaseModel):
    jpg = Column(Binary)


engine = Engine()
engine.bind(BaseModel)


def s3_upload(content: str) -> (str, str):
    # TODO persist in s3
    return "bucket-id", "key-id"


def b64sha256(content: str) -> str:
    hash = hashlib.sha256(content.encode())
    return base64.b64encode(hash.digest()).decode()


def new_paste(content: str) -> str:
    id = b64sha256(content)
Beispiel #23
0
def engine(dynamodb, dynamodbstreams):
    return Engine(dynamodb=dynamodb, dynamodbstreams=dynamodbstreams)
Beispiel #24
0
    product_id = Column(String, range_key=True, dynamo_name="pid")
    sku = Column(String, dynamo_name="sku")
    variation = Column(String, dynamo_name="v")
    weight = Column(String, dynamo_name="w")
    name = Column(String, dynamo_name="n")


class VendorImports(BaseModel):
    class Meta:
        table_name = "teascraper.VendorImports"

    vendor_id = Column(String, hash_key=True, dynamo_name="id")
    last_run_at = Column(DateTime, dynamo_name="lr")


engine = Engine()
models = [Product, VendorImports, ScrapedData]
# super hack to handle table setup
# in read-only prod environment
for model in models:
    try:
        engine.bind(model)
    except BloopException:
        print("Skipping table setup for " + model.__name__)
        engine.bind(model, skip_table_setup=True)


def make_product_id(vendor: str, name: str, weight: str):
    name = name.lower().replace(" ", "-").replace(".", "_")
    product_name = vendor + "." + name + "." + weight
    return product_name