Merge branch 'master' into fix-collectionChannel

This commit is contained in:
Alex Grin 2021-02-02 11:25:52 -05:00 committed by GitHub
commit bedcfc154b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 318 additions and 200 deletions

View file

@ -57,11 +57,11 @@ test:other-integration:
- pip install tox-travis - pip install tox-travis
- tox -e other - tox -e other
#test:json-api: test:json-api:
# stage: test stage: test
# script: script:
# - make install tools - make install tools
# - HOME=/tmp coverage run -p --source=lbry scripts/generate_json_api.py - HOME=/tmp coverage run -p --source=lbry scripts/generate_json_api.py

File diff suppressed because one or more lines are too long

View file

@ -2808,7 +2808,12 @@ class Daemon(metaclass=JSONRPCServerType):
for channel certificates, defaults to all accounts. for channel certificates, defaults to all accounts.
--wallet_id=<wallet_id> : (str) restrict operation to specific wallet --wallet_id=<wallet_id> : (str) restrict operation to specific wallet
Returns: {} Returns:
(dict) Signature if successfully made, (None) or an error otherwise
{
"signature": (str) The signature of the comment,
"signing_ts": (str) The timestamp used to sign the comment,
}
""" """
wallet = self.wallet_manager.get_wallet_or_default(wallet_id) wallet = self.wallet_manager.get_wallet_or_default(wallet_id)
assert not wallet.is_locked, "Cannot spend funds with locked wallet, unlock first." assert not wallet.is_locked, "Cannot spend funds with locked wallet, unlock first."

View file

@ -937,6 +937,11 @@ class Ledger(metaclass=LedgerRegistry):
return self.db.get_purchase_count(**constraints) return self.db.get_purchase_count(**constraints)
async def _resolve_for_local_results(self, accounts, txos): async def _resolve_for_local_results(self, accounts, txos):
txos = await self._resolve_for_local_claim_results(accounts, txos)
txos = await self._resolve_for_local_support_results(accounts, txos)
return txos
async def _resolve_for_local_claim_results(self, accounts, txos):
results = [] results = []
response = await self.resolve( response = await self.resolve(
accounts, [txo.permanent_url for txo in txos if txo.can_decode_claim] accounts, [txo.permanent_url for txo in txos if txo.can_decode_claim]
@ -952,6 +957,23 @@ class Ledger(metaclass=LedgerRegistry):
results.append(txo) results.append(txo)
return results return results
async def _resolve_for_local_support_results(self, accounts, txos):
channel_ids = set()
signed_support_txos = []
for txo in txos:
support = txo.can_decode_support
if support and support.signing_channel_id:
channel_ids.add(support.signing_channel_id)
signed_support_txos.append(txo)
if channel_ids:
channels = {
channel.claim_id: channel for channel in
(await self.claim_search(accounts, claim_ids=list(channel_ids)))[0]
}
for txo in signed_support_txos:
txo.channel = channels.get(txo.support.signing_channel_id)
return txos
async def get_claims(self, resolve=False, **constraints): async def get_claims(self, resolve=False, **constraints):
claims = await self.db.get_claims(**constraints) claims = await self.db.get_claims(**constraints)
if resolve: if resolve:

View file

@ -13,6 +13,7 @@ from lbry.extras.cli import set_kwargs, get_argument_parser
from lbry.extras.daemon.daemon import ( from lbry.extras.daemon.daemon import (
Daemon, jsonrpc_dumps_pretty, encode_pagination_doc Daemon, jsonrpc_dumps_pretty, encode_pagination_doc
) )
from tests.integration.other.test_comment_commands import MockedCommentServer
from lbry.extras.daemon.json_response_encoder import ( from lbry.extras.daemon.json_response_encoder import (
encode_tx_doc, encode_txo_doc, encode_account_doc, encode_file_doc, encode_tx_doc, encode_txo_doc, encode_account_doc, encode_file_doc,
encode_wallet_doc encode_wallet_doc
@ -66,6 +67,10 @@ class Examples(CommandTestCase):
async def asyncSetUp(self): async def asyncSetUp(self):
await super().asyncSetUp() await super().asyncSetUp()
self.daemon.conf.comment_server = 'http://localhost:2903/api'
self.comment_server = MockedCommentServer(2903)
await self.comment_server.start()
self.addCleanup(self.comment_server.stop)
self.recorder = ExampleRecorder(self) self.recorder = ExampleRecorder(self)
async def play(self): async def play(self):

View file

@ -461,6 +461,17 @@ class TransactionCommands(ClaimTestCase):
class TransactionOutputCommands(ClaimTestCase): class TransactionOutputCommands(ClaimTestCase):
async def test_txo_list_resolve_supports(self):
channel = self.get_claim_id(await self.channel_create('@identity'))
stream = self.get_claim_id(await self.stream_create())
support = await self.support_create(stream, channel_id=channel)
r, = await self.txo_list(type='support')
self.assertEqual(r['txid'], support['txid'])
self.assertNotIn('name', r['signing_channel'])
r, = await self.txo_list(type='support', resolve=True)
self.assertIn('name', r['signing_channel'])
self.assertEqual(r['signing_channel']['name'], '@identity')
async def test_txo_list_by_channel_filtering(self): async def test_txo_list_by_channel_filtering(self):
channel_foo = self.get_claim_id(await self.channel_create('@foo')) channel_foo = self.get_claim_id(await self.channel_create('@foo'))
channel_bar = self.get_claim_id(await self.channel_create('@bar')) channel_bar = self.get_claim_id(await self.channel_create('@bar'))

View file

@ -2,6 +2,7 @@ import re
import time import time
import typing import typing
from math import ceil from math import ceil
import secrets
from aiohttp import web from aiohttp import web
@ -50,7 +51,7 @@ class MockedCommentServer:
self.server = None self.server = None
self.comments = [] self.comments = []
self.reacts = {} self.reacts = {}
self.comment_id = 0 self.index = 0
self.react_id = 0 self.react_id = 0
@classmethod @classmethod
@ -66,14 +67,15 @@ class MockedCommentServer:
return schema return schema
def create_comment(self, claim_id=None, parent_id=None, channel_name=None, channel_id=None, **kwargs): def create_comment(self, claim_id=None, parent_id=None, channel_name=None, channel_id=None, **kwargs):
comment_id = self.comment_id comment_id = secrets.token_hex(64)
channel_url = 'lbry://' + channel_name + '#' + channel_id if channel_id else None channel_url = 'lbry://' + channel_name + '#' + channel_id if channel_id else None
if parent_id: if parent_id:
claim_id = self.comments[self.get_comment_id(parent_id)]['claim_id'] parent_comment = list(filter(lambda c: c['comment_id'] == parent_id, self.comments))[0]
claim_id = parent_comment['claim_id']
comment = self._create_comment( comment = self._create_comment(
comment_id=str(comment_id), comment_id=comment_id,
channel_name=channel_name, channel_name=channel_name,
channel_id=channel_id, channel_id=channel_id,
channel_url=channel_url, channel_url=channel_url,
@ -83,15 +85,14 @@ class MockedCommentServer:
**kwargs **kwargs
) )
self.comments.append(comment) self.comments.append(comment)
self.comment_id += 1
return self.clean(comment) return self.clean(comment)
def abandon_comment(self, comment_id: int, channel_id: str, **kwargs): def abandon_comment(self, comment_id: str, channel_id: str, **kwargs):
deleted = False deleted = False
comment_id = self.get_comment_id(comment_id) index = self.get_index_for_comment_id(comment_id)
try: try:
if self.comments[comment_id]['channel_id'] == channel_id: if index >= 0:
self.comments.pop(comment_id) self.comments.pop(index)
deleted = True deleted = True
finally: finally:
return { return {
@ -100,42 +101,41 @@ class MockedCommentServer:
} }
} }
def edit_comment(self, comment_id: typing.Union[str, int], comment: str, channel_id: str, def edit_comment(self, comment_id: str, comment: str, channel_id: str,
channel_name: str, signature: str, signing_ts: str) -> dict: channel_name: str, signature: str, signing_ts: str) -> dict:
edited = False edited = False
if self.credentials_are_valid(channel_id, channel_name, signature, signing_ts) \ if self.credentials_are_valid(channel_id, channel_name, signature, signing_ts) \
and self.is_valid_body(comment): and self.is_valid_body(comment):
comment_id = self.get_comment_id(comment_id) index = self.get_index_for_comment_id(comment_id)
if self.comments[comment_id]['channel_id'] == channel_id: if self.comments[index]['channel_id'] == channel_id:
self.comments[comment_id].update({ self.comments[index].update({
'comment': comment, 'comment': comment,
'signature': signature, 'signature': signature,
'signing_ts': signing_ts 'signing_ts': signing_ts
}) })
edited = True edited = True
return self.comments[comment_id] if edited else None return self.comments[index] if edited else None
def hide_comment(self, comment_id: typing.Union[int, str], signing_ts: str, signature: str): def hide_comment(self, comment_id: str, signing_ts: str, signature: str):
comment_id = self.get_comment_id(comment_id)
if self.is_signable(signature, signing_ts): if self.is_signable(signature, signing_ts):
self.comments[comment_id]['is_hidden'] = True self.comments[self.get_index_for_comment_id(comment_id)]['is_hidden'] = True
return True return True
return False return False
def pin_comment( def pin_comment(
self, self,
comment_id: typing.Union[int, str], comment_id: str,
channel_name: str, channel_id: str, remove: bool, channel_name: str, channel_id: str, remove: bool,
signing_ts: str, signature: str signing_ts: str, signature: str
): ):
comment_id = self.get_comment_id(comment_id) index = self.get_index_for_comment_id(comment_id)
if self.is_signable(signature, signing_ts): if self.is_signable(signature, signing_ts):
if remove: if remove:
self.comments[comment_id]['is_pinned'] = False self.comments[index]['is_pinned'] = False
else: else:
self.comments[comment_id]['is_pinned'] = True self.comments[index]['is_pinned'] = True
return self.comments[comment_id] return self.comments[index]
return False return False
def hide_comments(self, pieces: list): def hide_comments(self, pieces: list):
@ -164,15 +164,15 @@ class MockedCommentServer:
'has_hidden_comments': bool(list(filter(lambda x: x['is_hidden'], self.comments))) 'has_hidden_comments': bool(list(filter(lambda x: x['is_hidden'], self.comments)))
} }
def get_comment_channel_by_id(self, comment_id: int, **kwargs): def get_comment_channel_by_id(self, comment_id: str, **kwargs):
comment = self.comments[self.get_comment_id(comment_id)] comment = self.comments[self.get_index_for_comment_id(comment_id)]
return { return {
'channel_id': comment['channel_id'], 'channel_id': comment['channel_id'],
'channel_name': comment['channel_name'], 'channel_name': comment['channel_name'],
} }
def get_comments_by_id(self, comment_ids: list): def get_comments_by_id(self, comment_ids: list):
comments = [self.comments[self.get_comment_id(cid)] for cid in comment_ids] comments = [self.comments[self.get_index_for_comment_id(cid)] for cid in comment_ids]
return { return {
'page': 1, 'page': 1,
'page_size': len(comment_ids), 'page_size': len(comment_ids),
@ -331,17 +331,18 @@ class MockedCommentServer:
return 0 < len(comment) <= 2000 return 0 < len(comment) <= 2000
def is_valid_comment_id(self, comment_id: typing.Union[int, str]) -> bool: def is_valid_comment_id(self, comment_id: typing.Union[int, str]) -> bool:
if isinstance(comment_id, str) and comment_id.isalnum(): if isinstance(comment_id, str):
comment_id = int(comment_id) return True
if isinstance(comment_id, int):
return 0 <= comment_id < len(self.comments)
return False return False
def get_comment_id(self, cid: typing.Union[int, str, any]) -> int: def get_comment_for_id(self, cid: str) -> dict:
if not self.is_valid_comment_id(cid): return list(filter(lambda c: c['comment_id'] == cid, self.comments))[0]
raise ValueError('Comment ID is Invalid')
return cid if isinstance(cid, int) else int(cid) def get_index_for_comment_id(self, value: str):
for i, dic in enumerate(self.comments):
if dic['comment_id'] == value:
return i
return -1
@staticmethod @staticmethod
def claim_id_is_valid(claim_id: str) -> bool: def claim_id_is_valid(claim_id: str) -> bool:
@ -384,7 +385,6 @@ class MockedCommentServer:
(parent_id is None or self.is_valid_comment_id(parent_id)) (parent_id is None or self.is_valid_comment_id(parent_id))
class CommentCommands(CommandTestCase): class CommentCommands(CommandTestCase):
async def asyncSetUp(self): async def asyncSetUp(self):
@ -412,7 +412,7 @@ class CommentCommands(CommandTestCase):
self.assertEqual(stream['claim_id'], comments[0]['claim_id']) self.assertEqual(stream['claim_id'], comments[0]['claim_id'])
channel2 = (await self.channel_create('@BuffettJimmy'))['outputs'][0] channel2 = (await self.channel_create('@BuffettJimmy'))['outputs'][0]
await self.daemon.jsonrpc_comment_create( comment2 = await self.daemon.jsonrpc_comment_create(
claim_id=stream['claim_id'], claim_id=stream['claim_id'],
channel_name=channel2['name'], channel_name=channel2['name'],
comment='Let\'s all go to Margaritaville', comment='Let\'s all go to Margaritaville',
@ -712,7 +712,8 @@ class CommentCommands(CommandTestCase):
claim_id=claim_id, claim_id=claim_id,
channel_id=bee['claim_id'] channel_id=bee['claim_id']
) )
all_comments = [second_comment, first_comment] first_comment_id = first_comment['comment_id']
second_comment_id = second_comment['comment_id']
comment_list = await self.daemon.jsonrpc_comment_list(claim_id) comment_list = await self.daemon.jsonrpc_comment_list(claim_id)
self.assertEqual( self.assertEqual(
{'items', 'page', 'page_size', 'has_hidden_comments', 'total_items', 'total_pages'}, {'items', 'page', 'page_size', 'has_hidden_comments', 'total_items', 'total_pages'},
@ -721,29 +722,29 @@ class CommentCommands(CommandTestCase):
self.assertEqual(comment_list['total_items'], 2) self.assertEqual(comment_list['total_items'], 2)
bee_like_reaction = await self.daemon.jsonrpc_comment_react( bee_like_reaction = await self.daemon.jsonrpc_comment_react(
comment_ids=first_comment['comment_id'], comment_ids=first_comment_id,
channel_id=bee['claim_id'], channel_id=bee['claim_id'],
channel_name=bee['name'], channel_name=bee['name'],
react_type='like', react_type='like',
) )
moth_like_reaction = await self.daemon.jsonrpc_comment_react( moth_like_reaction = await self.daemon.jsonrpc_comment_react(
comment_ids=first_comment['comment_id'], comment_ids=first_comment_id,
channel_id=moth['claim_id'], channel_id=moth['claim_id'],
channel_name=moth['name'], channel_name=moth['name'],
react_type='like', react_type='like',
) )
reactions = await self.daemon.jsonrpc_comment_react_list( reactions = await self.daemon.jsonrpc_comment_react_list(
comment_ids=first_comment['comment_id'], comment_ids=first_comment_id,
channel_id=moth['claim_id'], channel_id=moth['claim_id'],
channel_name=moth['name'], channel_name=moth['name'],
) )
# {'my_reactions': {'0': {'like': 1}}, 'others_reactions': {'0': {'like': 1}}} # {'my_reactions': {'0': {'like': 1}}, 'others_reactions': {'0': {'like': 1}}}
self.assertEqual(reactions['my_reactions']['0']['like'], 1) self.assertEqual(reactions['my_reactions'][first_comment_id]['like'], 1)
self.assertEqual(reactions['others_reactions']['0']['like'], 1) self.assertEqual(reactions['others_reactions'][first_comment_id]['like'], 1)
bee_dislike_reaction = await self.daemon.jsonrpc_comment_react( bee_dislike_reaction = await self.daemon.jsonrpc_comment_react(
comment_ids=first_comment['comment_id'], comment_ids=first_comment_id,
channel_id=bee['claim_id'], channel_id=bee['claim_id'],
channel_name=bee['name'], channel_name=bee['name'],
react_type='dislike', react_type='dislike',
@ -751,30 +752,30 @@ class CommentCommands(CommandTestCase):
) )
reactions_after_bee_dislikes = await self.daemon.jsonrpc_comment_react_list( reactions_after_bee_dislikes = await self.daemon.jsonrpc_comment_react_list(
comment_ids=first_comment['comment_id'], comment_ids=first_comment_id,
channel_id=moth['claim_id'], channel_id=moth['claim_id'],
channel_name=moth['name'], channel_name=moth['name'],
) )
# {'my_reactions': {'0': {'like': 1, 'dislike', 0}}, 'others_reactions': {'0': {'like': 0, 'dislike': 1}}} # {'my_reactions': {'0': {'like': 1, 'dislike', 0}}, 'others_reactions': {'0': {'like': 0, 'dislike': 1}}}
self.assertEqual(reactions_after_bee_dislikes['my_reactions']['0']['like'], 1) self.assertEqual(reactions_after_bee_dislikes['my_reactions'][first_comment_id]['like'], 1)
self.assertEqual(reactions_after_bee_dislikes['my_reactions']['0']['dislike'], 0) self.assertEqual(reactions_after_bee_dislikes['my_reactions'][first_comment_id]['dislike'], 0)
self.assertEqual(reactions_after_bee_dislikes['others_reactions']['0']['dislike'], 1) self.assertEqual(reactions_after_bee_dislikes['others_reactions'][first_comment_id]['dislike'], 1)
self.assertEqual(reactions_after_bee_dislikes['others_reactions']['0']['like'], 0) self.assertEqual(reactions_after_bee_dislikes['others_reactions'][first_comment_id]['like'], 0)
only_likes_after_bee_dislikes = await self.daemon.jsonrpc_comment_react_list( only_likes_after_bee_dislikes = await self.daemon.jsonrpc_comment_react_list(
comment_ids=first_comment['comment_id'], comment_ids=first_comment_id,
channel_id=moth['claim_id'], channel_id=moth['claim_id'],
channel_name=moth['name'], channel_name=moth['name'],
react_types='like', react_types='like',
) )
self.assertEqual(only_likes_after_bee_dislikes['my_reactions']['0']['like'], 1) self.assertEqual(only_likes_after_bee_dislikes['my_reactions'][first_comment_id]['like'], 1)
self.assertEqual(only_likes_after_bee_dislikes['my_reactions']['0']['dislike'], 0) self.assertEqual(only_likes_after_bee_dislikes['my_reactions'][first_comment_id]['dislike'], 0)
self.assertEqual(only_likes_after_bee_dislikes['others_reactions']['0']['dislike'], 0) self.assertEqual(only_likes_after_bee_dislikes['others_reactions'][first_comment_id]['dislike'], 0)
self.assertEqual(only_likes_after_bee_dislikes['others_reactions']['0']['like'], 0) self.assertEqual(only_likes_after_bee_dislikes['others_reactions'][first_comment_id]['like'], 0)
bee_un_dislike_reaction = await self.daemon.jsonrpc_comment_react( bee_un_dislike_reaction = await self.daemon.jsonrpc_comment_react(
comment_ids=first_comment['comment_id'], comment_ids=first_comment_id,
channel_id=bee['claim_id'], channel_id=bee['claim_id'],
channel_name=bee['name'], channel_name=bee['name'],
remove=True, remove=True,
@ -782,30 +783,30 @@ class CommentCommands(CommandTestCase):
) )
reactions_after_bee_absconds = await self.daemon.jsonrpc_comment_react_list( reactions_after_bee_absconds = await self.daemon.jsonrpc_comment_react_list(
comment_ids=first_comment['comment_id'], comment_ids=first_comment_id,
channel_id=moth['claim_id'], channel_id=moth['claim_id'],
channel_name=moth['name'], channel_name=moth['name'],
) )
self.assertEqual(reactions_after_bee_absconds['my_reactions']['0']['like'], 1) self.assertEqual(reactions_after_bee_absconds['my_reactions'][first_comment_id]['like'], 1)
self.assertNotIn('dislike', reactions_after_bee_absconds['my_reactions']['0']) self.assertNotIn('dislike', reactions_after_bee_absconds['my_reactions'][first_comment_id])
self.assertEqual(reactions_after_bee_absconds['others_reactions']['0']['like'], 0) self.assertEqual(reactions_after_bee_absconds['others_reactions'][first_comment_id]['like'], 0)
self.assertNotIn('dislike', reactions_after_bee_absconds['others_reactions']['0']) self.assertNotIn('dislike', reactions_after_bee_absconds['others_reactions'][first_comment_id])
bee_reacts_to_both_comments = await self.daemon.jsonrpc_comment_react( bee_reacts_to_both_comments = await self.daemon.jsonrpc_comment_react(
comment_ids=first_comment['comment_id'] + ',' + second_comment['comment_id'], comment_ids=first_comment_id + ',' + second_comment_id,
channel_id=bee['claim_id'], channel_id=bee['claim_id'],
channel_name=bee['name'], channel_name=bee['name'],
react_type='frozen_tom', react_type='frozen_tom',
) )
reactions_after_double_frozen_tom = await self.daemon.jsonrpc_comment_react_list( reactions_after_double_frozen_tom = await self.daemon.jsonrpc_comment_react_list(
comment_ids=first_comment['comment_id'] + ',' + second_comment['comment_id'], comment_ids=first_comment_id + ',' + second_comment_id,
channel_id=moth['claim_id'], channel_id=moth['claim_id'],
channel_name=moth['name'], channel_name=moth['name'],
) )
self.assertEqual(reactions_after_double_frozen_tom['my_reactions']['0']['like'], 1) self.assertEqual(reactions_after_double_frozen_tom['my_reactions'][first_comment_id]['like'], 1)
self.assertNotIn('dislike', reactions_after_double_frozen_tom['my_reactions']['0']) self.assertNotIn('dislike', reactions_after_double_frozen_tom['my_reactions'][first_comment_id])
self.assertEqual(reactions_after_double_frozen_tom['others_reactions']['0']['frozen_tom'], 1) self.assertEqual(reactions_after_double_frozen_tom['others_reactions'][first_comment_id]['frozen_tom'], 1)
self.assertEqual(reactions_after_double_frozen_tom['others_reactions']['1']['frozen_tom'], 1) self.assertEqual(reactions_after_double_frozen_tom['others_reactions'][second_comment_id]['frozen_tom'], 1)