Пример #1
0
async def post_document(request, parent=None):
    payload = request.json
    if "pid" not in payload or not isinstance(payload["pid"], str):
        return response.json({"error": "need_pid_string"}, status=400)
    if "title" not in payload or not isinstance(payload["title"], str):
        return response.json({"error": "need_title_string"}, status=400)
    if "published" in payload and not isinstance(payload["published"], bool):
        return response.json({"error": "invalid_published_type"}, status=400)

    async with pg.transaction() as conn:
        node = await conn.fetchrow(t_document_hist.insert().values(
            pid=payload["pid"],
            title=payload["title"],
            published=payload.get("published", False),
        ).returning(t_document_hist.c.hid, t_document_hist.c.tstamp))
        edge = await conn.fetchrow(t_document_event.insert().values(
            parent=parent,
            hist=node["hid"],
            uid=request["session"]["uid"],
            reason="insert" if parent is None else "update",
        ).returning(t_document_event.c.tstamp))
    return response.json({
        "hid": str(node["hid"]),
        "content_tstamp": node["tstamp"] and
                          node["tstamp"].strftime("%Y-%m-%dT%H:%M:%S.%fZ"),
        "action_tstamp": edge["tstamp"].strftime("%Y-%m-%dT%H:%M:%S.%fZ"),
    })
Пример #2
0
async def put_edge_event(request, parent=None, hist=None):
    async with pg.transaction() as conn:
        await conn.execute(t_document_event.delete().where(
            (t_document_event.c.parent == parent) &
            (t_document_event.c.hist == hist)
        ))
        await conn.fetchrow(t_document_event.insert().values(
            parent=parent,
            hist=hist,
            **request.json # TODO: validate the input
        ))
    return response.json({"status": "replaced"})
Пример #3
0
async def put_node(request, hid):
    async with pg.transaction() as conn:
        await conn.execute(
            t_document_hist.delete().where(t_document_hist.c.hid == hid)
        )
        node = await conn.fetchrow(t_document_hist.insert().values(
            **request.json # TODO: validate the input
        ).returning(t_document_hist.c.hid))
    return response.json({
        "status": "replaced",
        "hid": str(node["hid"]),
    })
Пример #4
0
async def increment_metrics(feature: str, env: str, date_: date):
    upsert = insert(db.metrics).values(feature=feature, env=env, date=date_) \
        .on_conflict_do_update(
            index_elements=[db.metrics.c.env, db.metrics.c.feature, db.metrics.c.date],
            set_={'hit_count': text('metrics.hit_count + 1')}
    )

    try:
        async with pg.transaction() as conn:
            await conn.fetch(upsert)
    except Exception:
        traceback.print_exc()
        pass
Пример #5
0
    async def dislike_channel(client: ClientConnection, message: dict):
        # TODO: must be merged with like_channel according to new API
        if not client.is_initialised():
            API._log("Uninitialised client tried to dislike a channel")

            await client.send_error(
                message['id'], 401,
                "client must initialise before attempting to dislike a channel"
            )
            return

        sel_q = select([Channel
                        ]).where(Channel.username == message["username"])
        channel = await pg.fetchrow(sel_q)
        if channel is None:
            await client.send_error(message['id'], 404,
                                    "channel does not exist")
            return

        # TODO: take user's IP into account to make fake likes adding harder or allow to like only authorized users
        # TODO: There is an error - can't undo like or dislike
        async with pg.transaction() as conn:
            sel_q = select([ChannelSessionAction]). \
                where(and_(ChannelSessionAction.channel_id == channel['id'],
                           ChannelSessionAction.session_id == client.session['id'])).with_for_update()
            channel_session_action = await conn.fetchrow(sel_q)
            if channel_session_action is None:
                ins_q = insert(ChannelSessionAction).values(
                    channel_id=channel['id'],
                    session_id=client.session['id'],
                    like=False)
                await conn.fetchrow(ins_q)
            else:
                if not channel_session_action['like']:
                    await client.send_error(message['id'], 403,
                                            "channel already disliked")
                    return
                upd_q = update(ChannelSessionAction).where(ChannelSessionAction.id == channel_session_action['id']). \
                    values(like=False)
                await conn.fetchrow(upd_q)

            upd_q = update(Channel).where(Channel.id == channel['id']).values(
                likes=Channel.likes - 1)
            await conn.fetchrow(upd_q)

        API._log(f'Disliked channel {message["username"]}')

        await client.send_response({
            'id': message['id'],
            'action': message['action']
        })
Пример #6
0
async def set_toggle_state(env, feature, state, rollout_days=0):
    results = await pg.fetch(db.toggles.select().where(
        db.toggles.c.feature == feature).where(db.toggles.c.env == env))

    reply = {'toggle': {'env': env, 'feature': feature, 'state': state}}

    if results and results[0]['state'] == state:
        # nothing to do
        return reply
    elif not results and state == 'OFF':
        # nothing to do
        return reply

    # delete the toggle first to clean up any data
    async with pg.transaction() as conn:
        await conn.fetchval(db.toggles.delete().where(
            db.toggles.c.feature == feature).where(db.toggles.c.env == env))

        schedule = {}
        if state == 'ROLL':
            schedule['increment'] = 1
            schedule['hours_count'] = 0
            schedule['total_hours'] = int(float(rollout_days)) * 24
            schedule['current_percent'] = 1
        elif state == 'PAUSE':
            roll_schedule = json.loads(results[0]['schedule'])
            schedule['hours_count'] = 0
            schedule['total_hours'] = 48
            schedule['current_percent'] = roll_schedule.get('current_percent')
            schedule['rolling_state'] = roll_schedule
        else:
            schedule['dirty'] = True

        if state in ('ON', 'ROLL', 'PAUSE'):
            await conn.fetchval(db.toggles.insert().values(
                feature=feature,
                env=env,
                state=state,
                date_on=functions.now(),
                schedule=schedule))
        elif state == 'OFF':
            # nothing to do, already deleted
            pass

    return reply
Пример #7
0
async def get_graph(request, hid):
    cte_query = t_document_event.select() \
                                .where(t_document_event.c.hist == str(hid)) \
                                .cte("all_events", recursive=True)
    ref_query = cte_query.alias("ref")
    evt_query = t_document_event.alias("evt")
    full_events_query = cte_query.union(
        select(
            columns=[evt_query],
            from_obj=[ref_query, evt_query],
            whereclause=(evt_query.c.parent == ref_query.c.hist) |
                        (evt_query.c.hist == ref_query.c.parent),
        )
    ).select().order_by(cte_query.c.tstamp)

    async with pg.transaction(isolation="repeatable_read") as conn:
        edges = await conn.fetch(full_events_query)
        nodes = await conn.fetch(t_document_hist.select().where(
            t_document_hist.c.hid.in_([r["hist"] for r in edges])
        ).order_by(t_document_hist.c.tstamp))
    return response.json({
        "nodes": [{
            "hid": str(node["hid"]),
            "pid": node["pid"],
            "title": node["title"],
            "tstamp": node["tstamp"] and
                      node["tstamp"].strftime("%Y-%m-%dT%H:%M:%S.%fZ"),
        } for node in nodes],
        "edges": [{
            "parent": edge["parent"] and str(edge["parent"]),
            "hist": str(edge["hist"]),
            "reason": edge["reason"],
            "comment": edge["comment"],
            "tstamp": edge["tstamp"].strftime("%Y-%m-%dT%H:%M:%S.%fZ"),
        } for edge in edges],
    })
Пример #8
0
async def test_transaction():
    await _init()

    async with pg.transaction() as conn:
        for row in await conn.fetch(query):
            assert row.a == 4.0
Пример #9
0
    async def modify_channel(client: ClientConnection, message: dict):
        """
        Get available channel tags list
        :param client:
        :param dict message:
        """
        # Check is user authorized
        if not client.is_authorized():
            await client.send_error(
                message['id'], 401,
                "client must login before attempting to modify a channel")
            return

        # Check channel exists in our DB and verified
        # Check user is admin
        sel_q = select([Channel]).\
            select_from(outerjoin(Channel, ChannelAdmin)).\
            where(and_(Channel.username == message['username'],
                       Channel.verified == True,
                       ChannelAdmin.admin_id == client.session['client_id']))
        API._log('DBG: %s' % sel_q)
        res = await pg.fetch(sel_q)
        if not res:
            await client.send_error(
                message['id'], 404,
                'Channel is not found or not verified or user is not admin')
            return
        channel = dict(res.items())

        updated = {}
        # Check category change
        if 'category_id' in message:
            updated['category_id'] = message['category_id']
        # Check mutual promotion changed
        if 'mut_promo' in message:
            updated['mutual_promotion'] = message['mut_promo']
        # Check cost changed
        if 'cost' in message:
            updated['cost'] = message['cost']
        # Check language changed
        if 'language' in message:
            updated['language'] = message['language']
        # Check description changed
        if 'description' in message:
            updated['description'] = message['description']
        # Check tags changed
        if 'tags' in message:
            # TODO: custom tags can be defined for premium channels
            # istartswith analog here
            sel_q = select([Tag]).select_from(Tag).\
                where(func.lower(Tag.name).in_([tag.lower() for tag in message['tags']]))
            API._log('DBG: %s' % sel_q)
            tags = await pg.fetch(sel_q)
            async with pg.transaction() as conn:
                # Delete current tags
                del_q = delete(ChannelTag).where(
                    ChannelTag.channel_id == channel['id'])
                await conn.fetchrow(del_q)
                # Insert new ones
                for tag in tags:
                    ins_q = insert(ChannelTag).values(channel_id=channel['id'],
                                                      tag_id=tag['id'])
                    await conn.fetchrow(ins_q)
        else:
            # if not changed, just get current ones
            sel_q = select([Tag]).select_from(outerjoin(
                ChannelTag, Tag)).where(ChannelTag.channel_id == channel['id'])
            tags = await pg.fetch(sel_q)
        channel['tags'] = [tag['name'] for tag in tags]

        if updated:
            upd_q = update(Channel).where(Channel.id == channel['id']).values(
                **updated)
            await pg.fetchrow(upd_q)
            channel.update(updated)

        # Return channel object
        await client.send_response({
            'data': channel,
            'id': message['id'],
            'action': message['action']
        })
Пример #10
0
    async def update_channel(client: ClientConnection, message: dict):
        # Getting general channel info
        response = await Telegram.send_telegram_request(
            bot_token=Telegram.get_bot_token(),
            method="getChat",
            params={"chat_id": message["username"]})
        chat = response.get('result', None)
        if not chat:
            await client.send_error(message['id'], 404,
                                    "channel does not exist")
            return
        if chat['type'] != 'channel':
            await client.send_error(message['id'], 403,
                                    'Not a channel, but %s' % chat['type'])
            return

        # Get channel members count
        chat["members"] = (await Telegram.send_telegram_request(
            bot_token=Telegram.get_bot_token(),
            method="getChatMembersCount",
            params={"chat_id": message["username"]}))["result"]

        # Get channel admin user list via special public bot
        admins = (await Telegram.send_telegram_request(
            bot_token=Telegram.get_admin_bot_token(),
            method="getChatAdministrators",
            params={"chat_id": message["username"]}))
        admins = admins.get('result', [])
        for admin in admins:
            # Bots are not welcome here
            if admin['user']['is_bot']:
                continue

            user_info = admin['user']
            user_info['photo'] = await Telegram.get_user_profile_photo(
                bot_token=Telegram.get_bot_token(),
                user_id=user_info['id'],
            )

        # Try to get channel photo
        if "photo" in chat:
            chat["photo"] = await Telegram.get_telegram_file(
                bot_token=Telegram.get_bot_token(),
                file_id=chat["photo"]["big_file_id"])

        # Update DB..
        sel_q = select([Channel]).where(Channel.telegram_id == chat["id"])
        channel = await pg.fetchrow(sel_q)
        channel_dict = {
            'telegram_id': chat["id"],
            'username': "******" + chat["username"],
            'title': chat["title"],
            'photo': chat.get("photo", None),
            'description': chat.get("description", None),
            'members': chat["members"]
        }
        if channel is None:
            ins_q = insert(Channel).values(**channel_dict).returning(
                Channel.id)
            channel_id = await pg.fetchval(ins_q)
        else:
            channel_id = channel['id']
            upd_q = update(Channel).where(Channel.id == channel_id).values(
                **channel_dict)
            await pg.fetchrow(upd_q)

        if admins:
            async with pg.transaction() as conn:
                if channel is not None:
                    # Delete current admins for existing channel
                    del_q = delete(ChannelAdmin).where(
                        ChannelAdmin.channel_id == channel_id)
                    await conn.fetchrow(del_q)

                # Populate admin list
                for admin in admins:
                    user_info = admin.pop('user')
                    sel_q = select([Client
                                    ]).where(Client.user_id == user_info['id'])
                    db_client = await conn.fetchrow(sel_q)
                    if db_client is None:
                        ins_q = insert(Client).values(
                            user_id=user_info["id"],
                            first_name=user_info["first_name"],
                            username=user_info.get("username", None),
                            photo=user_info.get("photo",
                                                None)).returning(Client.id)
                        admin_id = await conn.fetchval(ins_q)
                    else:
                        admin_id = db_client['id']

                    ins_q = insert(ChannelAdmin).values(
                        channel_id=channel_id,
                        admin_id=admin_id,
                        owner=admin['status'] == 'creator',
                        raw=admin)
                    await conn.fetchrow(ins_q)

        API._log('%s channel "%s"' %
                 ('Added' if channel is None else 'Updated', chat['username']))

        await client.send_response({
            'id': message['id'],
            'action': message['action']
        })