예제 #1
0
class Token(Base):

    __acl__ = (
        Allow(Everyone, Create, Fields()),
        Allow(Everyone, Read, Fields(
            'id',
            'claimed'
        )),
        Allow(Everyone, Update, Fields(
            'claimed',
            'account_id',
            'account_provider_id'
        ))
    )
    __table_args__ = (
        sql.PrimaryKeyConstraint(
            'id'),
        sql.ForeignKeyConstraint(
            ['account_id', 'account_provider_id'],
            ['account.id', 'account.provider_id'])
    )

    id = sql.Column(sql.String(16), default=functools.partial(gen_token, 6))

    claimed = sql.Column(sql.Boolean, default=False)

    account_provider_id = sql.Column(sql.String(16))
    account_id = sql.Column(sql.String(32))
    account = orm.relation(
        'Account',
        single_parent=True)
    parent = orm.synonym('account')
예제 #2
0
파일: base.py 프로젝트: ynplayer/api
    def fields(self, value):
        """Fields can be a set of column names and include dotted syntax.

        A dotted field notation like `foo.bar` instructs the model to look up
        its `foo` relation and render its `bar` attribute.

            {'foo': {'bar': 42}}

        If the `foo` relation is one to many, the `bar` attribute is rendered
        from all the members in `foo`.

            {'foo': [{'bar': 73}, {'bar': 89}]}
        """
        tree = {}
        flat = []
        for field in value:
            key, *path = field.split('.', 1)
            flat.append(key)
            if path:
                tree[key] = tree.get(key, []) + list(path)
        for field, paths in tree.items():
            should_expand, is_list = self._inspect_field(field)
            if should_expand:
                relations = getattr(self, field)
                if not is_list:
                    relations = [relations]
                for relation in relations:
                    if isinstance(relation, Base):
                        relation.fields = Fields(*paths)
        self._fields = Fields(*flat)
예제 #3
0
class FavouriteItem(TracklistItemMixin, Base):

    __acl__ = (Allow(
        Parent, Create,
        Fields('favourite_id', 'favourite_provider_id', 'account_id',
               'account_provider_id', 'track_id', 'track_provider_id')),
               Allow(Owner, Read, Fields('id', 'track_id',
                                         'track_provider_id')),
               Allow(Owner, Delete), Allow(Parent, Query, Fields('favourite')))
    __table_args__ = (sql.PrimaryKeyConstraint('id'),
                      sql.ForeignKeyConstraint(
                          ['favourite_provider_id', 'favourite_id'],
                          ['favourite.provider_id', 'favourite.id']),
                      sql.ForeignKeyConstraint(
                          ['account_id', 'account_provider_id'],
                          ['account.id', 'account.provider_id']),
                      sql.ForeignKeyConstraint(['track_provider_id'],
                                               ['provider.id']))

    provider_id = orm.synonym('favourite_provider_id')

    favourite_id = sql.Column(sql.String(96), nullable=False)
    favourite_provider_id = sql.Column(sql.String(16), nullable=False)
    favourite = orm.relation('Favourite', back_populates='items')
    parent = orm.synonym('favourite')
예제 #4
0
class Session(Base):

    __acl__ = (
        Allow(Owner, Create, Fields(
            'account_id',
            'account_provider_id',
            'system',
            'browser',
            'screen'
        )),
        Allow(Owner, Read, Fields()),
        Deny()
    )
    __table_args__ = (
        sql.PrimaryKeyConstraint(
            'id'),
        sql.ForeignKeyConstraint(
            ['account_id', 'account_provider_id'],
            ['account.id', 'account.provider_id'])
    )

    id = sql.Column(sql.String(64), default=functools.partial(gen_token, 64))

    account_id = sql.Column(sql.String(32), nullable=False)
    account_provider_id = sql.Column(sql.String(16), nullable=False)
    account = orm.relation(
        'Account',
        back_populates='sessions',
        uselist=False,
        single_parent=True)
    parent = orm.synonym('account')

    system = sql.Column(sql.String(64), nullable=False)
    browser = sql.Column(sql.String(64), nullable=False)
    screen = sql.Column(sql.String(32), nullable=False)
예제 #5
0
def test_available_fields_are_always_contained_in_specific_fields():
    target = mock.Mock()
    f1 = Available(target)
    f2 = Fields('ten', 'six', 'two')
    assert f1 in f2
    f1 = Available(target)
    f2 = Fields()
    assert f1 in f2
예제 #6
0
def test_fields_should_check_field_containment_against_values():
    fields = Fields('one', 'two', 'six')
    assert 'one' in fields
    assert 'zero' not in fields
    f1 = Fields('one', 'five')
    assert f1 not in fields
    f2 = Fields('one', 'two', 'six', 'nine')
    assert f2 not in fields
    f3 = Fields('one', 'six')
    assert f3 in fields
예제 #7
0
파일: provider.py 프로젝트: ynplayer/api
class Provider(Base):

    __acl__ = (Allow(Everyone, Read, Fields('id', 'client_id')),
               Allow(Everyone, Query, Fields('id')))
    __table_args__ = (sql.PrimaryKeyConstraint('id'), )

    id = sql.Column(sql.String(12))

    provider_id = orm.synonym('id')

    @property
    def client_id(self):
        if self.id in opt.options['providers']:
            return opt.options[self.id]['api_key']
예제 #8
0
class TrackComment(Transient):

    __acl__ = (
        Allow(Everyone, Read, Fields(
            'id',
            'provider_id',
            'account.id',
            'account.provider_id',
            'account.title',
            'account.image.small',
            'account.image.medium',
            'account.image.large',
            'body',
            'timestamp',
            'track_id',
            'track_provider_id',
            'created'
        )),
    )

    id = None
    provider_id = None

    account = None

    body = None
    timestamp = None
    track_id = None
    track_provider_id = None
예제 #9
0
파일: base.py 프로젝트: ynplayer/api
    def event_hook(redis_pool, method, mapper, connection, target):
        target.fields = Fields(*target.__fields__)
        cache = redis.Redis(connection_pool=redis_pool)
        for pattern in target.__channel__:
            channel = pattern.format(**target.__dict__)
            message = json.dumps(
                {
                    'channel': channel,
                    'method': method,
                    'body': target
                },
                cls=Encoder)

            start = time.time()
            try:
                cache.publish(channel, message)
            except redis.exceptions.ConnectionError:
                status_code = 503
                host = '::1'
            else:
                status_code = 200
                host = cache.connection_pool.connection_kwargs['host']

            pub_time = 1000.0 * (time.time() - start)
            app_log.info('{} REDIS {} {} ({}) {:.2f}ms'.format(
                status_code, method.upper(), channel, host, pub_time))
예제 #10
0
파일: image.py 프로젝트: yayanheeh/api
class Image(Base):

    __acl__ = (Allow(
        Everyone, Read,
        Fields('id', 'small', 'medium', 'large', 'created',
               'updated')), Deny())
    __table_args__ = (sql.PrimaryKeyConstraint('id'), )

    id = sql.Column(sql.Integer)
    small = sql.Column(sql.String(256))
    medium = sql.Column(sql.String(256))
    large = sql.Column(sql.String(256), nullable=False)

    def copy(self):
        return Image(small=self.small, medium=self.medium, large=self.large)

    @classmethod
    def from_soundcloud(cls, url):
        if isinstance(url, str):
            return cls(small=url,
                       medium=url.replace('large', 't300x300'),
                       large=url.replace('large', 't500x500'))

    @classmethod
    def from_youtube(cls, thumbnails):
        if isinstance(thumbnails, dict):
            return cls(small=thumbnails.get('default', {}).get('url'),
                       medium=thumbnails.get('medium', {}).get('url'),
                       large=thumbnails.get('high', {}).get('url'))
예제 #11
0
def test_controller_should_raise_not_found_on_failed_update(db, current_user):
    controller = MyController(db, current_user, Account, mock.Mock())
    ids = {'id': 'does-not-exist', 'provider_id': 'unheard-of'}
    kw = {'title': 'foo', 'refresh_token': 'bar'}
    with pytest.raises(ControllerException) as error:
        yield controller.update(ids, kw, Fields('user_id', 'title'))
    assert error.value.status_code == 404
예제 #12
0
def test_fields_should_be_eager_with_values_and_lazy_with_targets():
    fields = Fields('one', 'two', 'six')
    assert fields._target is None
    assert fields._values == {'one', 'two', 'six'}
    target = object()
    assert fields in fields(target)
    assert fields(target)._target is target
    assert set(fields) == {'one', 'two', 'six'}
예제 #13
0
def test_controller_should_search_using_query_and_read_all_entities(
        db, current_user, account):
    controller = MyController(db, current_user, Account, mock.Mock())
    ids = {'id': account.id, 'provider_id': account.provider_id}
    entities = yield controller.search(ids, {}, Fields('id'))

    assert controller.policy.grant_read.call_args[0][:-1] == (account,
                                                              entities)
    assert set(controller.policy.grant_read.call_args[0][-1]) == {'id'}
예제 #14
0
def test_controller_should_read_entity_by_the_books(db, current_user, account):
    controller = MyController(db, current_user, Account, mock.Mock())
    ids = {'id': account.id, 'provider_id': account.provider_id}
    entity = yield controller.read(ids, Fields('title', 'provider_id'))
    assert entity is account

    assert controller.policy.grant_read.call_args[0][:-1] == (account, entity)
    assert set(controller.policy.grant_read.call_args[0][-1]) == {
        'provider_id', 'title'
    }
예제 #15
0
파일: favourite.py 프로젝트: ynplayer/api
class Favourite(TracklistMixin, Base):

    __acl__ = (Allow(
        Owner, Read,
        Fields('id', 'provider_id', 'account_id', 'account_provider_id')), )

    account = orm.relation('Account',
                           back_populates='favourite',
                           viewonly=True)

    items = orm.relation('FavouriteItem',
                         cascade='all, delete-orphan',
                         order_by='FavouriteItem.created',
                         single_parent=True)
예제 #16
0
파일: image.py 프로젝트: ynplayer/api
class Image(Base):

    __acl__ = (Allow(
        Everyone, Read,
        Fields('id', 'small', 'medium', 'large', 'created',
               'updated')), Deny())
    __table_args__ = (sql.PrimaryKeyConstraint('id'), )

    id = sql.Column(sql.Integer)
    small = sql.Column(sql.String(256))
    medium = sql.Column(sql.String(256))
    large = sql.Column(sql.String(256), nullable=False)

    def copy(self):
        return Image(small=self.small, medium=self.medium, large=self.large)
예제 #17
0
def test_controller_should_update_entity_and_read_result(
        db, current_user, account):
    controller = MyController(db, current_user, Account, mock.Mock())
    ids = {'id': account.id, 'provider_id': account.provider_id}
    kw = {'title': 'foo', 'refresh_token': 'bar'}
    entity = yield controller.update(ids, kw, Fields('user_id', 'title'))
    assert entity is account
    assert sqlalchemy.orm.util.object_state(entity).persistent

    assert controller.policy.grant_update.call_args[0][:-1] == (account,
                                                                entity)
    assert set(controller.policy.grant_update.call_args[0][-1]) == {
        'title', 'refresh_token'
    }

    assert controller.policy.grant_read.call_args[0][:-1] == (account, entity)
    assert set(
        controller.policy.grant_read.call_args[0][-1]) == {'user_id', 'title'}
예제 #18
0
class User(Base):

    __acl__ = (Allow(
        Child, Read,
        Fields('id', 'provider_id', 'accounts.id', 'accounts.provider_id',
               'accounts.connected', 'accounts.favourite_id',
               'accounts.image.id', 'accounts.image.small',
               'accounts.image.medium', 'accounts.image.large',
               'accounts.title', 'created', 'updated')), )
    __table_args__ = (sql.PrimaryKeyConstraint('id'), )

    id = sql.Column(sql.Integer)
    provider_id = 'cloudplayer'

    accounts = orm.relation('Account',
                            back_populates='user',
                            uselist=True,
                            single_parent=True,
                            cascade='all, delete-orphan')
    children = orm.synonym('accounts')
예제 #19
0
def test_controller_should_create_entity_and_read_result(
        db, current_user, account, user):
    controller = MyController(db, current_user, Account, mock.Mock())
    ids = {'id': '1234', 'provider_id': 'cloudplayer'}
    kw = {'title': 'foo', 'access_token': 'bar', 'user_id': user.id}
    entity = yield controller.create(ids, kw, Fields('id', 'title'))
    assert entity.id == '1234'
    assert entity.provider_id == 'cloudplayer'
    assert entity.title == 'foo'
    assert entity.access_token == 'bar'
    assert sqlalchemy.orm.util.object_state(entity).persistent

    assert controller.policy.grant_create.call_args[0][:-1] == (account,
                                                                entity)
    assert set(controller.policy.grant_create.call_args[0][-1]) == {
        'provider_id', 'title', 'title', 'access_token', 'id', 'user_id'
    }

    assert controller.policy.grant_read.call_args[0][:-1] == (account, entity)
    assert set(
        controller.policy.grant_read.call_args[0][-1]) == {'id', 'title'}
예제 #20
0
class Track(Transient):

    __acl__ = (Allow(
        Everyone, Read,
        Fields('id', 'provider_id', 'account.id', 'account.provider_id',
               'account.title', 'account.image.small', 'account.image.medium',
               'account.image.large', 'aspect_ratio', 'duration',
               'favourite_count', 'image.small', 'image.medium', 'image.large',
               'play_count', 'title', 'created')), )

    id = None
    provider_id = None

    account = None

    aspect_ratio = None
    duration = None
    favourite_count = None
    image = None
    play_count = None
    title = None
예제 #21
0
class Playlist(TracklistMixin, Base):

    __acl__ = (
        Allow(Owner, Create, Fields(
            'provider_id',
            'account_id',
            'account_provider_id',
            'description',
            'public',
            'title'
        )),
        Allow(Owner, Read, Fields(
            'id',
            'provider_id',
            'account_id',
            'account_provider_id',
            'description',
            'follower_count',
            'image.id',
            'image.small',
            'image.medium',
            'image.large',
            'public',
            'title',
            'created',
            'updated'
        )),
        Allow(Owner, Update, Fields(
            'description',
            'public',
            'title'
        )),
        Allow(Owner, Delete),
        Allow(Owner, Query, Fields(
            'id',
            'provider_id',
            'account_id',
            'account_provider_id'
        )),
        Deny()
    )
    __channel__ = (
        'playlist.{provider_id}.{id}',
    )
    __fields__ = (
        'id',
        'provider_id',
        'account_id',
        'account_provider_id',
        'description',
        'follower_count',
        'image.id',
        'image.small',
        'image.medium',
        'image.large',
        'public',
        'title',
        'created',
        'updated'
    )

    @declared_attr
    def __table_args__(cls):
        return super().__table_args__ + (
            sql.ForeignKeyConstraint(
                ['image_id'],
                ['image.id']),
        )

    account = orm.relation(
        'Account',
        back_populates='playlists',
        cascade='all',
        viewonly=True)
    parent = orm.synonym('account')

    items = orm.relation(
        'PlaylistItem',
        cascade='all, delete-orphan',
        order_by='PlaylistItem.rank',
        single_parent=True)

    description = sql.Column(sql.Unicode(5120), nullable=True)
    follower_count = sql.Column(sql.Integer, default=0)
    public = sql.Column(sql.Boolean, default=False)
    title = sql.Column(sql.Unicode(256), nullable=False)

    image_id = sql.Column(sql.Integer)
    image = orm.relation(
        'Image',
        cascade='all, delete-orphan',
        single_parent=True,
        uselist=False)
예제 #22
0
파일: base.py 프로젝트: ynplayer/api
 def fields(self):
     return getattr(self, '_fields', Fields())
예제 #23
0
class Track(Transient):

    __acl__ = (
        Allow(Everyone, Read, Fields(
            'id',
            'provider_id',
            'account.id',
            'account.provider_id',
            'account.title',
            'account.image.small',
            'account.image.medium',
            'account.image.large',
            'aspect_ratio',
            'duration',
            'favourite_count',
            'image.small',
            'image.medium',
            'image.large',
            'play_count',
            'title',
            'created'
        )),
    )

    id = None
    provider_id = None

    account = None

    aspect_ratio = None
    duration = None
    favourite_count = None
    image = None
    play_count = None
    title = None

    @classmethod
    def from_provider(cls, provider_id, track):
        if provider_id == 'soundcloud':
            return cls.from_soundcloud(track)
        elif provider_id == 'youtube':
            return cls.from_youtube(track)
        else:
            raise ValueError('unsupported provider')

    @classmethod
    def from_soundcloud(cls, track):
        user = track['user']
        artist = Account(
            id=user['id'],
            provider_id='soundcloud',
            title=user['username'],
            image=Image.from_soundcloud(user.get('avatar_url')))

        return cls(
            id=track['id'],
            provider_id='soundcloud',
            account=artist,
            aspect_ratio=1.0,
            duration=int(track['duration'] / 1000.0),
            favourite_count=track.get('favoritings_count', 0),
            image=Image.from_soundcloud(track.get('artwork_url')),
            play_count=track.get('playback_count', 0),
            title=track['title'],
            created=datetime.datetime.strptime(
                track['created_at'], '%Y/%m/%d %H:%M:%S %z'))

    @classmethod
    def from_youtube(cls, track):
        snippet = track['snippet']
        player = track['player']
        statistics = track['statistics']
        duration = isodate.parse_duration(track['contentDetails']['duration'])

        artist = Account(
            id=snippet['channelId'],
            provider_id='youtube',
            image=None,
            title=snippet['channelTitle'])

        return cls(
            id=track['id'],
            provider_id='youtube',
            account=artist,
            aspect_ratio=(
                float(player['embedHeight']) / float(player['embedWidth'])),
            duration=int(duration.total_seconds()),
            favourite_count=statistics.get('likeCount', 0),
            image=Image.from_youtube(snippet.get('thumbnails')),
            play_count=statistics.get('viewCount', 0),
            title=snippet['title'],
            created=datetime.datetime.strptime(
                snippet['publishedAt'], '%Y-%m-%dT%H:%M:%S.%fZ'))
예제 #24
0
파일: account.py 프로젝트: ynplayer/api
class Account(Base):

    __acl__ = (Allow(
        Owner, Read,
        Fields('id', 'provider_id', 'user_id', 'connected', 'favourite_id',
               'image.id', 'image.small', 'image.medium', 'image.large',
               'title', 'created', 'updated')),
               Allow(Owner, Update, Fields('image', 'title')),
               Allow(
                   Everyone, Read,
                   Fields('id', 'provider_id', 'image.id', 'image.small',
                          'image.medium', 'image.large', 'title')),
               Allow(Everyone, Query, Fields('id', 'provider_id',
                                             'title')), Deny())
    __fields__ = ('id', 'provider_id', 'user_id', 'connected', 'favourite_id',
                  'image_id', 'title', 'created', 'updated')
    __channel__ = ('account.{provider_id}.{id}', )
    __table_args__ = (sql.PrimaryKeyConstraint('id', 'provider_id'),
                      sql.ForeignKeyConstraint(['provider_id'],
                                               ['provider.id']),
                      sql.ForeignKeyConstraint(['user_id'], ['user.id']),
                      sql.ForeignKeyConstraint(['image_id'], ['image.id']))

    id = sql.Column(sql.String(32))
    account_id = orm.synonym('id')

    provider_id = sql.Column(sql.String(16), nullable=False)
    provider = orm.relation('Provider',
                            cascade=None,
                            uselist=False,
                            viewonly=True)
    account_provider_id = orm.synonym('provider_id')

    user_id = sql.Column(sql.Integer, nullable=False)
    user = orm.relation('User',
                        back_populates='accounts',
                        uselist=False,
                        viewonly=True)
    parent = orm.synonym('user')

    sessions = orm.relation('Session',
                            back_populates='account',
                            cascade='all, delete-orphan',
                            single_parent=True,
                            uselist=True)

    image_id = sql.Column(sql.Integer)
    image = orm.relation('Image',
                         cascade='all, delete-orphan',
                         single_parent=True,
                         uselist=False)

    @property
    def favourite_id(self):
        if self.favourite:
            return self.favourite.id

    favourite = orm.relation('Favourite',
                             back_populates='account',
                             cascade='all, delete-orphan',
                             single_parent=True,
                             uselist=False)

    playlists = orm.relation('Playlist',
                             back_populates='account',
                             cascade='all, delete-orphan',
                             single_parent=True,
                             uselist=True)

    title = sql.Column('title', sql.Unicode(64))

    access_token = sql.Column(sql.String(256))
    refresh_token = sql.Column(sql.String(256))
    token_expiration = sql.Column(sql.DateTime())

    @property
    def connected(self):
        return self.provider_id == 'cloudplayer' or all(
            [self.access_token, self.refresh_token])