Example #1
0
    def test_do(self):
        ref = self._create()["ref"]
        self.assertEqual(self._q(query.do(query.delete(ref), 1)), 1)
        self.assertFalse(self._q(query.exists(ref)))

        self.assertEqual(self._q(query.do(1)), 1)
        self.assertEqual(self._q(query.do(1, 2)), 2)
        self.assertEqual(self._q(query.do([1, 2])), [1, 2])
Example #2
0
def update_documents(sql_query: sql.SQLQuery) -> QueryExpression:
    """Update document fields with the given values.

    Params:
    -------
    table: Table object that contains the parameters for building an update query in FQL.

    Returns:
    --------
    An FQL update query for the given collection and documents.
    """
    assert len(sql_query.tables) == 1
    table = sql_query.tables[0]
    assert len(sql_query.filter_groups) <= 1
    filter_group = (None if not any(sql_query.filter_groups) else
                    sql_query.filter_groups[0])

    field_updates = {column.name: column.value for column in table.columns}
    return q.let(
        {"document_set": build_document_set_intersection(table, filter_group)},
        q.do(
            q.update(
                q.select(
                    "ref",
                    q.get(q.var("document_set")),
                ),
                {"data": field_updates},
            ),
            {"data": [{
                "count": q.count(q.var("document_set"))
            }]},
        ),
    )
Example #3
0
def create_user(data):
    try:
        current_identity = get_current_identity()
        email_hash = md5(
            current_identity['data']['username'].encode('utf-8')).hexdigest()
        return current_app.fauna_client.query(
            q.if_(
                q.is_ref(
                    q.select_with_default(['data', 'user'],
                                          q.get(q.current_identity()), None)),
                q.abort('exists'),
                q.let(
                    {
                        'userMetaRef': q.new_id(),
                        'userRef': q.new_id()
                    },
                    q.do(
                        q.create(
                            q.ref(q.collection('user_metas'),
                                  q.var('userMetaRef')),
                            {
                                'data': {
                                    'name':
                                    data.get('name'),
                                    'email':
                                    q.select(['data', 'username'],
                                             q.get(q.current_identity())),
                                    'dob':
                                    parser.parse(data.get('dob')).date()
                                }
                            }),
                        q.create(
                            q.ref(q.collection('users'),
                                  q.var('userRef')),
                            {
                                'data': {
                                    'alias':
                                    data.get('alias'),
                                    'avatar':
                                    f'https://www.gravatar.com/avatar/{email_hash}',
                                    'public':
                                    False,
                                    'meta':
                                    q.ref(q.collection('user_metas'),
                                          q.var('userMetaRef')),
                                }
                            }),
                        q.update(
                            q.current_identity(), {
                                'data': {
                                    'user':
                                    q.ref(q.collection('users'),
                                          q.var('userRef'))
                                }
                            }), q.call('current_user', [])))))
    except Exception as e:
        if str(e) == 'exists':
            abort(409, 'User for current identity already exists.')
        print(e)
Example #4
0
def _delete_data():
    resources = [q.functions(), q.indexes(), q.collections()]
    delete = lambda res: q.foreach(
        q.lambda_("res", q.delete(q.var("res"))), q.paginate(res)
    )
    delete_queries = [delete(res) for res in resources]

    _execute_with_retries(q.do(*delete_queries))
Example #5
0
def _translate_create_index(statement: token_groups.Statement,
                            idx: int) -> typing.List[QueryExpression]:
    _, unique = statement.token_next_by(m=(token_types.Keyword, "UNIQUE"),
                                        idx=idx)
    idx, _ = statement.token_next_by(m=(token_types.Keyword, "ON"), idx=idx)
    _, index_params = statement.token_next_by(i=token_groups.Function, idx=idx)

    params_idx, table_identifier = index_params.token_next_by(
        i=token_groups.Identifier)
    table_name = table_identifier.value

    params_idx, column_identifiers = index_params.token_next_by(
        i=token_groups.Parenthesis, idx=params_idx)

    index_fields = [
        token.value for token in column_identifiers.flatten()
        if token.ttype == token_types.Name
    ]

    if len(index_fields) > 1:
        raise exceptions.NotSupportedError(
            "Creating indexes for multiple columns is not currently supported."
        )

    index_terms = [{
        "field": ["data", index_field]
    } for index_field in index_fields]
    index_name = fql.index_name(table_name,
                                column_name=index_fields[0],
                                index_type=fql.IndexType.TERM)

    return [
        q.do(
            q.if_(
                # We automatically create indices for some fields on collection creation,
                # so we can skip explicit index creation if it already exists.
                q.exists(q.index(index_name)),
                None,
                q.create_index({
                    "name": index_name,
                    "source": q.collection(table_name),
                    "terms": index_terms,
                    "unique": unique,
                }),
            ),
            q.let(
                {"collection": q.collection(table_name)},
                {"data": [{
                    "id": q.var("collection")
                }]},
            ),
        )
    ]
Example #6
0
def _make_sure_information_schema_exists() -> typing.List[QueryExpression]:
    index_queries = [
        _create_table_indices(collection_name, field_metadata)
        for collection_name, field_metadata in
        INFORMATION_SCHEMA_COLLECTIONS.items()
    ]
    return [
        q.if_(
            q.exists(q.collection("information_schema_tables_")),
            None,
            q.do(*[
                q.create_collection({"name": collection_name})
                for collection_name in INFORMATION_SCHEMA_COLLECTIONS
            ]),
        ),
        q.if_(
            q.exists(q.index(fql.index_name("information_schema_tables_"))),
            None,
            q.do(*index_queries),
        ),
    ]
Example #7
0
    def unsubscribe_from_anime(self, anime_doc_id: str):
        try:
            anime = client.query(
                q.get(q.ref(q.collection(animes), anime_doc_id)))
            client.query(
                q.let(
                    {
                        'anime_ref':
                        q.ref(q.collection(animes), anime_doc_id),
                        'bot_user':
                        q.ref(q.collection(users), self.chat_id),
                        'followers':
                        q.select(['data', 'followers'],
                                 q.get(q.var('anime_ref'))),
                    },
                    q.do(
                        q.update(
                            q.var('anime_ref'), {
                                'data': {
                                    'followers': q.subtract(
                                        q.var('followers'), 1)
                                }
                            }),
                        q.update(
                            q.var('bot_user'), {
                                'data': {
                                    'animes_watching':
                                    q.filter_(
                                        q.lambda_(
                                            'watched_anime_ref',
                                            q.not_(
                                                q.equals(
                                                    q.var('watched_anime_ref'),
                                                    q.var('anime_ref')))),
                                        q.select(['data', 'animes_watching'],
                                                 q.get(q.var('bot_user'))))
                                }
                            }),
                        q.if_(q.equals(q.var('followers'), 1),
                              q.delete(q.var('anime_ref')), 'successful!'))))

            updater.bot.send_message(chat_id=self.chat_id,
                                     text='You have stopped following ' +
                                     anime['data']['title'])
        except errors.NotFound:
            logger.info(
                'Somehow, a user {0} almost unsubscribed from an anime that did not exist'
                .format(self.chat_id))
        except Exception as err:
            log_error(err)
Example #8
0
def create_transaction(client, num_customers, max_txn_amount):
    #
    # This method is going to create a random transaction that moves a random amount
    # from a source customer to a destination customer. Prior to committing the transaction
    # a check will be performed to insure that the source customer has a sufficient balance
    # to cover the amount and not go into an overdrawn state.
    #
    uuid = uuid4().urn[9:]

    source_id = randint(1, num_customers)
    dest_id = randint(1, num_customers)
    while dest_id == source_id:
        dest_id = randint(1, num_customers)
    amount = randint(1, max_txn_amount)

    transaction = {"uuid": uuid, "sourceCust": source_id, "destCust": dest_id , "amount": amount}

    res = client.query(
        q.let(
            {"source_customer": q.get(q.match(q.index("customer_by_id"), source_id)),
             "dest_customer": q.get(q.match(q.index("customer_by_id"), dest_id))},
            q.let(
                {"source_balance": q.select(["data", "balance"], q.var("source_customer")),
                 "dest_balance": q.select(["data", "balance"], q.var("dest_customer"))},
                q.let(
                    {"new_source_balance": q.subtract(q.var("source_balance"), amount),
                     "new_dest_balance": q.add(q.var("dest_balance"), amount)},
                    q.if_(
                        q.gte(q.var("new_source_balance"), 0),
                        q.do(
                            q.create(q.class_("transactions"), {"data": transaction}),
                            q.update(q.select("ref", q.var("source_customer")),
                                     {"data": {"txnID": uuid, "balance": q.var("new_source_balance")}}),
                            q.update(q.select("ref", q.var("dest_customer")),
                                     {"data": {"txnID": uuid, "balance": q.var("new_dest_balance")}})
                        ),
                        "Error. Insufficient funds."
                    )
                )
            )
        )
    )
Example #9
0
def _translate_create_table(
        statement: token_groups.Statement,
        table_token_idx: int) -> typing.List[QueryExpression]:
    idx, table_identifier = statement.token_next_by(i=token_groups.Identifier,
                                                    idx=table_token_idx)
    table_name = table_identifier.value

    idx, column_identifiers = statement.token_next_by(
        i=token_groups.Parenthesis, idx=idx)

    field_metadata = _extract_column_definitions(column_identifiers)
    index_queries = _create_table_indices(table_name, field_metadata)

    collection_metadata: CollectionMetadata = {
        "fields": field_metadata,
        "indexes": _create_index_metadata(table_name, field_metadata),
    }
    information_metadata_query = _update_information_metadata(
        table_name, collection_metadata)

    # Fauna creates resources asynchronously, so we cannot create and use a collection
    # in the same transaction, so we have to run the expressions that create
    # the collection and the indices that depend on it separately
    return [
        *_make_sure_information_schema_exists(),
        q.create_collection({"name": table_name}),
        q.do(
            *index_queries,
            information_metadata_query,
            q.let(
                {"collection": q.collection(table_name)},
                {"data": [{
                    "id": q.var("collection")
                }]},
            ),
        ),
    ]
Example #10
0
def translate_drop(
        statement: token_groups.Statement) -> typing.List[QueryExpression]:
    """Translate a DROP SQL query into an equivalent FQL query.

    Params:
    -------
    statement: An SQL statement returned by sqlparse.

    Returns:
    --------
    An FQL query expression.
    """
    idx, _ = statement.token_next_by(m=(token_types.Keyword, "TABLE"))
    _, table_identifier = statement.token_next_by(i=token_groups.Identifier,
                                                  idx=idx)
    table_name = table_identifier.value

    deleted_collection = q.select("ref", q.delete(q.collection(table_name)))
    return [
        q.do(
            q.map_(
                q.lambda_("ref", q.delete(q.var("ref"))),
                q.paginate(
                    q.union(
                        q.match(
                            q.index(
                                fql.index_name(
                                    "information_schema_tables_",
                                    column_name="name_",
                                    index_type=fql.IndexType.TERM,
                                )),
                            table_name,
                        ),
                        fql.convert_to_ref_set(
                            "information_schema_columns_",
                            q.range(
                                q.match(
                                    q.index(
                                        fql.index_name(
                                            "information_schema_columns_",
                                            column_name="table_name_",
                                            index_type=fql.IndexType.VALUE,
                                        ))),
                                [table_name],
                                [table_name],
                            ),
                        ),
                        fql.convert_to_ref_set(
                            "information_schema_indexes_",
                            q.range(
                                q.match(
                                    q.index(
                                        fql.index_name(
                                            "information_schema_indexes_",
                                            column_name="table_name_",
                                            index_type=fql.IndexType.VALUE,
                                        ))),
                                [table_name],
                                [table_name],
                            ),
                        ),
                    ), ),
            ),
            q.let(
                {"collection": deleted_collection},
                {"data": [{
                    "id": q.var("collection")
                }]},
            ),
        )
    ]
 def test_do(self):
     self.assertJson(query.do(query.add(1, 2), query.var("x")),
                     '{"do":[{"add":[1,2]},{"var":"x"}]}')
     self.assertJson(query.do(1), '{"do":[1]}')
     self.assertJson(query.do(1, 2), '{"do":[1,2]}')
     self.assertJson(query.do([1, 2]), '{"do":[[1,2]]}')
Example #12
0
def _update_information_metadata(
        table_name: str,
        collection_metadata: CollectionMetadata) -> QueryExpression:
    column_metadata = [{
        "name_": "id",
        "table_name_": table_name,
        "type_": "Integer",
        "nullable": False,
        "default_": None,
    }]

    column_metadata.extend([
        {
            "name_": name,
            "table_name_": table_name,
            "type_": metadata["type"],
            # A bit awkward, but SQL uses the 'NOT NULL' keyword, while SQLAlchemy
            # uses 'nullable' when returning metadata
            "nullable_": not metadata["not_null"],
            "default_": metadata["default"],
        } for name, metadata in collection_metadata["fields"].items()
    ])

    index_metadata = [{
        "name_": index_name,
        "table_name_": table_name,
        **typing.cast(typing.Dict[str, typing.Any], metadata),
    } for index_name, metadata in collection_metadata["indexes"].items()]

    return q.if_(
        # We don't want to update information schema collections with information schema info,
        # because that would some weird inception-type stuff.
        q.contains_str_regex(
            table_name, r"^information_schema_(?:tables|columns|indexes)_$"),
        None,
        q.do(
            q.create(
                q.collection("information_schema_tables_"),
                {"data": {
                    "name_": table_name
                }},
            ),
            q.foreach(
                q.lambda_(
                    "column_metadata",
                    q.create(
                        q.collection("information_schema_columns_"),
                        {"data": q.var("column_metadata")},
                    ),
                ),
                column_metadata,
            ),
            q.foreach(
                q.lambda_(
                    "index_metadata",
                    q.create(
                        q.collection("information_schema_indexes_"),
                        {"data": q.var("index_metadata")},
                    ),
                ),
                index_metadata,
            ),
        ),
    )
Example #13
0
    def subscribe_to_anime(self, anime_link: str):
        try:
            # create a new anime document
            anime_info = anime_alarm.utils.GGAScraper().get_anime_info(
                anime_link)

            print(anime_info['anime_id'])

            result = client.query(
                q.let(
                    {
                        'user_anime_list':
                        q.select(['data', 'animes_watching'],
                                 q.get(q.ref(q.collection(users),
                                             self.chat_id))),
                    },
                    q.if_(
                        # check if this anime exists in the db
                        q.exists(
                            q.match(q.index(anime_by_id),
                                    anime_info['anime_id'])),

                        # if it exists...
                        q.let(
                            {
                                'anime_ref':
                                q.select(
                                    'ref',
                                    q.get(
                                        q.match(q.index(anime_by_id),
                                                anime_info['anime_id'])))
                            },
                            q.if_(
                                # check if user has subscribed to this anime already
                                q.contains_value(q.var('anime_ref'),
                                                 q.var('user_anime_list')),
                                'This anime is already on your watch list!',
                                q.do(
                                    q.update(
                                        q.ref(q.collection(users),
                                              self.chat_id),
                                        {
                                            'data': {
                                                'animes_watching':
                                                q.append(
                                                    q.var('user_anime_list'),
                                                    [q.var('anime_ref')])
                                            }
                                        }),
                                    q.update(
                                        q.var('anime_ref'), {
                                            'data': {
                                                'followers':
                                                q.add(
                                                    q.select([
                                                        'data', 'followers'
                                                    ], q.get(
                                                        q.var('anime_ref'))),
                                                    1)
                                            }
                                        }),
                                ))),
                        q.let(
                            {'new_anime_id': q.new_id()},
                            q.do(
                                # create new anime document
                                q.create(
                                    q.ref(q.collection(animes),
                                          q.var('new_anime_id')),
                                    {
                                        'data': {
                                            'title':
                                            anime_info['title'],
                                            'followers':
                                            1,
                                            'link':
                                            anime_link,
                                            'anime_id':
                                            anime_info['anime_id'],
                                            'anime_alias':
                                            anime_info['anime_alias'],
                                            'episodes':
                                            anime_info['number_of_episodes'],
                                            'last_episode': {
                                                'link':
                                                anime_info[
                                                    'latest_episode_link'],
                                                'title':
                                                anime_info[
                                                    'latest_episode_title'],
                                            },
                                        }
                                    }),
                                # add to user's list of subscribed animes
                                q.update(
                                    q.ref(q.collection(users), self.chat_id), {
                                        'data': {
                                            'animes_watching':
                                            q.append(
                                                q.var('user_anime_list'), [
                                                    q.ref(
                                                        q.collection(animes),
                                                        q.var('new_anime_id'))
                                                ])
                                        }
                                    }),
                            )))))

            if isinstance(result, str):
                updater.bot.send_message(chat_id=self.chat_id, text=result)
            else:
                updater.bot.send_message(
                    chat_id=self.chat_id,
                    text='You are now listening for updates on ' +
                    anime_info['title'])
        except Exception as err:
            log_error(err)