fixes and tests for publish command

This commit is contained in:
Lex Berezhny 2019-03-27 16:02:17 -04:00
parent 148481a5ca
commit 7860b956ff
3 changed files with 87 additions and 21 deletions

View file

@ -1967,7 +1967,7 @@ class Daemon(metaclass=JSONRPCServerType):
Create or update a stream claim at a given name (use 'stream create/update' for more control). Create or update a stream claim at a given name (use 'stream create/update' for more control).
Usage: Usage:
publish (<name> | --name=<name>) (<bid> | --bid=<bid>) (<file_path> | --file_path=<file_path>) publish (<name> | --name=<name>) [--bid=<bid>] [--file_path=<file_path>]
[<stream_type> | --stream_type=<stream_type>] [--tags=<tags>...] [<stream_type> | --stream_type=<stream_type>] [--tags=<tags>...]
[--fee_currency=<fee_currency>] [--fee_amount=<fee_amount>] [--fee_address=<fee_address>] [--fee_currency=<fee_currency>] [--fee_amount=<fee_amount>] [--fee_address=<fee_address>]
[--title=<title>] [--description=<description>] [--author=<author>] [--language=<language>] [--title=<title>] [--description=<description>] [--author=<author>] [--language=<language>]
@ -1975,7 +1975,8 @@ class Daemon(metaclass=JSONRPCServerType):
[--release_time=<release_time>] [--release_time=<release_time>]
[--video_width=<video_width>] [--video_height=<video_height>] [--video_duration=<video_duration>] [--video_width=<video_width>] [--video_height=<video_height>] [--video_duration=<video_duration>]
[--image_width=<image_width>] [--image_height=<image_height>] [--audio_duration=<audio_duration>] [--image_width=<image_width>] [--image_height=<image_height>] [--audio_duration=<audio_duration>]
[--channel_id=<channel_id>] [--channel_account_id=<channel_account_id>...] [--channel_id=<channel_id>] [--channel_name=<channel_name>]
[--channel_account_id=<channel_account_id>...]
[--account_id=<account_id>] [--claim_address=<claim_address>] [--preview] [--account_id=<account_id>] [--claim_address=<claim_address>] [--preview]
Options: Options:
@ -2011,6 +2012,7 @@ class Daemon(metaclass=JSONRPCServerType):
--audio_duration=<duration> : (int) audio duration in seconds, an attempt will be made to --audio_duration=<duration> : (int) audio duration in seconds, an attempt will be made to
calculate this automatically if not provided calculate this automatically if not provided
--channel_id=<channel_id> : (str) claim id of the publisher channel --channel_id=<channel_id> : (str) claim id of the publisher channel
--channel_name=<channel_name> : (str) name of publisher channel
--channel_account_id=<channel_id>: (str) one or more account ids for accounts to look in --channel_account_id=<channel_id>: (str) one or more account ids for accounts to look in
for channel certificates, defaults to all accounts. for channel certificates, defaults to all accounts.
--account_id=<account_id> : (str) account to use for funding the transaction --account_id=<account_id> : (str) account to use for funding the transaction
@ -2022,6 +2024,10 @@ class Daemon(metaclass=JSONRPCServerType):
account = self.get_account_or_default(kwargs.get('account_id')) account = self.get_account_or_default(kwargs.get('account_id'))
claims = await account.get_claims(claim_name=name) claims = await account.get_claims(claim_name=name)
if len(claims) == 0: if len(claims) == 0:
if 'bid' not in kwargs:
raise Exception("'bid' is a required argument for new publishes.")
if 'file_path' not in kwargs:
raise Exception("'file_path' is a required argument for new publishes.")
return await self.jsonrpc_stream_create(name, **kwargs) return await self.jsonrpc_stream_create(name, **kwargs)
elif len(claims) == 1: elif len(claims) == 1:
assert claims[0].claim.is_stream, f"Claim at name '{name}' is not a stream claim." assert claims[0].claim.is_stream, f"Claim at name '{name}' is not a stream claim."
@ -2035,7 +2041,7 @@ class Daemon(metaclass=JSONRPCServerType):
conditions=[WALLET_IS_UNLOCKED]) conditions=[WALLET_IS_UNLOCKED])
async def jsonrpc_stream_create( async def jsonrpc_stream_create(
self, name, bid, file_path, allow_duplicate_name=False, self, name, bid, file_path, allow_duplicate_name=False,
channel_id=None, channel_account_id=None, channel_id=None, channel_name=None, channel_account_id=None,
account_id=None, claim_address=None, preview=False, **kwargs): account_id=None, claim_address=None, preview=False, **kwargs):
""" """
Make a new stream claim and announce the associated file to lbrynet. Make a new stream claim and announce the associated file to lbrynet.
@ -2050,7 +2056,8 @@ class Daemon(metaclass=JSONRPCServerType):
[--release_time=<release_time>] [--release_time=<release_time>]
[--video_width=<video_width>] [--video_height=<video_height>] [--video_duration=<video_duration>] [--video_width=<video_width>] [--video_height=<video_height>] [--video_duration=<video_duration>]
[--image_width=<image_width>] [--image_height=<image_height>] [--audio_duration=<audio_duration>] [--image_width=<image_width>] [--image_height=<image_height>] [--audio_duration=<audio_duration>]
[--channel_id=<channel_id>] [--channel_account_id=<channel_account_id>...] [--channel_id=<channel_id>] [--channel_name=<channel_name>]
[--channel_account_id=<channel_account_id>...]
[--account_id=<account_id>] [--claim_address=<claim_address>] [--preview] [--account_id=<account_id>] [--claim_address=<claim_address>] [--preview]
Options: Options:
@ -2097,7 +2104,7 @@ class Daemon(metaclass=JSONRPCServerType):
""" """
self.valid_stream_name_or_error(name) self.valid_stream_name_or_error(name)
account = self.get_account_or_default(account_id) account = self.get_account_or_default(account_id)
channel = await self.get_channel_or_none(channel_account_id, channel_id, for_signing=True) channel = await self.get_channel_or_none(channel_account_id, channel_id, channel_name, for_signing=True)
amount = self.get_dewies_or_error('bid', bid, positive_value=True) amount = self.get_dewies_or_error('bid', bid, positive_value=True)
claim_address = await self.get_receiving_address(claim_address, account) claim_address = await self.get_receiving_address(claim_address, account)
kwargs['fee_address'] = self.get_fee_address(kwargs, claim_address) kwargs['fee_address'] = self.get_fee_address(kwargs, claim_address)
@ -2141,7 +2148,7 @@ class Daemon(metaclass=JSONRPCServerType):
conditions=[WALLET_IS_UNLOCKED]) conditions=[WALLET_IS_UNLOCKED])
async def jsonrpc_stream_update( async def jsonrpc_stream_update(
self, claim_id, bid=None, file_path=None, self, claim_id, bid=None, file_path=None,
channel_id=None, channel_account_id=None, clear_channel=False, channel_id=None, channel_name=None, channel_account_id=None, clear_channel=False,
account_id=None, claim_address=None, account_id=None, claim_address=None,
preview=False, **kwargs): preview=False, **kwargs):
""" """
@ -2149,14 +2156,15 @@ class Daemon(metaclass=JSONRPCServerType):
Usage: Usage:
stream_update (<claim_id> | --claim_id=<claim_id>) stream_update (<claim_id> | --claim_id=<claim_id>)
[--bid=<bid>] [--file_path=<file_path>] [--tags=<tags>...] [--clear-tags] [--bid=<bid>] [--file_path=<file_path>] [--tags=<tags>...] [--clear_tags]
[--fee_currency=<fee_currency>] [--fee_amount=<fee_amount>] [--fee_address=<fee_address>] [--fee_currency=<fee_currency>] [--fee_amount=<fee_amount>] [--fee_address=<fee_address>]
[--title=<title>] [--description=<description>] [--author=<author>] [--language=<language>] [--title=<title>] [--description=<description>] [--author=<author>] [--language=<language>]
[--license=<license>] [--license_url=<license_url>] [--thumbnail_url=<thumbnail_url>] [--license=<license>] [--license_url=<license_url>] [--thumbnail_url=<thumbnail_url>]
[--release_time=<release_time>] [--stream_type=<stream_type>] [--release_time=<release_time>] [--stream_type=<stream_type>]
[--video_width=<video_width>] [--video_height=<video_height>] [--video_duration=<video_duration>] [--video_width=<video_width>] [--video_height=<video_height>] [--video_duration=<video_duration>]
[--image_width=<image_width>] [--image_height=<image_height>] [--audio_duration=<audio_duration>] [--image_width=<image_width>] [--image_height=<image_height>] [--audio_duration=<audio_duration>]
[--channel_id=<channel_id>] [--channel_account_id=<channel_account_id>...] [--clear-channel] [--channel_id=<channel_id>] [--channel_name=<channel_name>] [--clear_channel]
[--channel_account_id=<channel_account_id>...]
[--account_id=<account_id>] [--claim_address=<claim_address>] [--preview] [--account_id=<account_id>] [--claim_address=<claim_address>] [--preview]
Options: Options:
@ -2164,7 +2172,7 @@ class Daemon(metaclass=JSONRPCServerType):
--bid=<bid> : (decimal) amount to back the claim --bid=<bid> : (decimal) amount to back the claim
--file_path=<file_path> : (str) path to file to be associated with name. --file_path=<file_path> : (str) path to file to be associated with name.
--tags=<tags> : (list) content tags --tags=<tags> : (list) content tags
--clear-tags : (bool) clear existing tags (prior to adding new ones) --clear_tags : (bool) clear existing tags (prior to adding new ones)
--fee_currency=<fee_currency> : (string) specify fee currency --fee_currency=<fee_currency> : (string) specify fee currency
--fee_amount=<fee_amount> : (decimal) content download fee --fee_amount=<fee_amount> : (decimal) content download fee
--fee_address=<fee_address> : (str) address where to send fee payments, will use --fee_address=<fee_address> : (str) address where to send fee payments, will use
@ -2191,7 +2199,7 @@ class Daemon(metaclass=JSONRPCServerType):
--audio_duration=<duration> : (int) audio duration in seconds, an attempt will be made to --audio_duration=<duration> : (int) audio duration in seconds, an attempt will be made to
calculate this automatically if not provided calculate this automatically if not provided
--channel_id=<channel_id> : (str) claim id of the publisher channel --channel_id=<channel_id> : (str) claim id of the publisher channel
--clear-channel : (bool) remove channel signature --clear_channel : (bool) remove channel signature
--channel_account_id=<channel_id>: (str) one or more account ids for accounts to look in --channel_account_id=<channel_id>: (str) one or more account ids for accounts to look in
for channel certificates, defaults to all accounts. for channel certificates, defaults to all accounts.
--account_id=<account_id> : (str) account to use for funding the transaction --account_id=<account_id> : (str) account to use for funding the transaction
@ -2223,8 +2231,8 @@ class Daemon(metaclass=JSONRPCServerType):
claim_address = old_txo.get_address(account.ledger) claim_address = old_txo.get_address(account.ledger)
channel = None channel = None
if channel_id: if channel_id or channel_name:
channel = await self.get_channel_or_error(channel_account_id, channel_id, for_signing=True) channel = await self.get_channel_or_error(channel_account_id, channel_id, channel_name, for_signing=True)
elif old_txo.claim.is_signed and not clear_channel: elif old_txo.claim.is_signed and not clear_channel:
channel = old_txo.channel channel = old_txo.channel
@ -2974,22 +2982,28 @@ class Daemon(metaclass=JSONRPCServerType):
self.valid_address_or_error(address) self.valid_address_or_error(address)
return address return address
async def get_channel_or_none(self, account_ids: List[str], channel_id: str = None, async def get_channel_or_none(self, account_ids: List[str], channel_id: str = None, channel_name: str = None,
for_signing: bool = False) -> Output: for_signing: bool = False) -> Output:
if channel_id is not None: if channel_id is not None:
return await self.get_channel_or_error(account_ids, channel_id, for_signing) return await self.get_channel_or_error(account_ids, channel_id, channel_name, for_signing)
async def get_channel_or_error(self, account_ids: List[str], channel_id: str = None, async def get_channel_or_error(self, account_ids: List[str], channel_id: str = None, channel_name: str = None,
for_signing: bool = False) -> Output: for_signing: bool = False) -> Output:
if channel_id is None: if channel_id:
raise ValueError("Couldn't find channel because a channel_id was not provided.") key, value = 'id', channel_id
elif channel_name:
key, value = 'name', channel_name
else:
raise ValueError("Couldn't find channel because a channel_id or channel_name was not provided.")
for account in self.get_accounts_or_all(account_ids): for account in self.get_accounts_or_all(account_ids):
channels = await account.get_channels(claim_id=channel_id, limit=1) channels = await account.get_channels(**{f'claim_{key}': value}, limit=1)
if channels: if len(channels) == 1:
if for_signing and channels[0].private_key is None: if for_signing and channels[0].private_key is None:
raise Exception(f"Couldn't find private key for channel '{channel_id}'. ") raise Exception(f"Couldn't find private key for {key} '{value}'. ")
return channels[0] return channels[0]
raise ValueError(f"Couldn't find channel with channel_id '{channel_id}'.") elif len(channels) > 1:
raise ValueError(f"Multiple channels found with {key} '{value}', pass a channel_id to narrow it down.")
raise ValueError(f"Couldn't find channel with {key} '{value}'.")
def get_account_or_default(self, account_id: str, argument_name: str = "account", lbc_only=True) -> LBCAccount: def get_account_or_default(self, account_id: str, argument_name: str = "account", lbc_only=True) -> LBCAccount:
if account_id is None: if account_id is None:

View file

@ -1,4 +1,6 @@
import hashlib import hashlib
import tempfile
import logging
from binascii import unhexlify from binascii import unhexlify
import base64 import base64
@ -246,8 +248,10 @@ class StreamCommands(CommandTestCase):
# defaults to using all accounts to lookup channel # defaults to using all accounts to lookup channel
await self.stream_create('hovercraft1', channel_id=baz_id) await self.stream_create('hovercraft1', channel_id=baz_id)
self.assertEqual((await self.claim_search('hovercraft1'))[0]['channel_name'], '@baz')
# uses only the specific accounts which contains the channel # uses only the specific accounts which contains the channel
await self.stream_create('hovercraft2', channel_id=baz_id, channel_account_id=[account2_id]) await self.stream_create('hovercraft2', channel_id=baz_id, channel_account_id=[account2_id])
self.assertEqual((await self.claim_search('hovercraft2'))[0]['channel_name'], '@baz')
# fails when specifying account which does not contain channel # fails when specifying account which does not contain channel
with self.assertRaisesRegex(ValueError, "Couldn't find channel with channel_id"): with self.assertRaisesRegex(ValueError, "Couldn't find channel with channel_id"):
await self.stream_create( await self.stream_create(
@ -386,6 +390,45 @@ class StreamCommands(CommandTestCase):
await self.claim_abandon(tx['outputs'][0]['claim_id']) await self.claim_abandon(tx['outputs'][0]['claim_id'])
await self.assertBalance(self.account, '9.97968399') await self.assertBalance(self.account, '9.97968399')
async def test_publish(self):
# errors on missing arguments to create a stream
with self.assertRaisesRegex(Exception, "'bid' is a required argument for new publishes."):
await self.daemon.jsonrpc_publish('foo')
with self.assertRaisesRegex(Exception, "'file_path' is a required argument for new publishes."):
await self.daemon.jsonrpc_publish('foo', bid='1.0')
# successfully create stream
with tempfile.NamedTemporaryFile() as file:
file.write(b'hi')
file.flush()
tx1 = await self.publish('foo', bid='1.0', file_path=file.name)
# doesn't error on missing arguments when doing an update stream
tx2 = await self.publish('foo', tags='updated')
self.assertEqual(
tx1['outputs'][0]['claim_id'],
tx2['outputs'][0]['claim_id']
)
# update conflict with two claims of the same name
tx3 = await self.stream_create('foo', allow_duplicate_name=True)
with self.assertRaisesRegex(Exception, "There are 2 claims for 'foo'"):
await self.daemon.jsonrpc_publish('foo')
# abandon duplicate channel
await self.claim_abandon(tx3['outputs'][0]['claim_id'])
# publish to a channel
await self.channel_create('@abc')
tx3 = await self.publish('foo', channel_name='@abc')
r = await self.resolve('lbry://@abc/foo')
self.assertEqual(
r['lbry://@abc/foo']['claim']['claim_id'],
tx3['outputs'][0]['claim_id']
)
async def test_claim_search(self): async def test_claim_search(self):
# search for channel claim # search for channel claim
channel = await self.channel_create('@abc', '1.0') channel = await self.channel_create('@abc', '1.0')

View file

@ -186,6 +186,15 @@ class CommandTestCase(IntegrationTestCase):
await self.on_transaction_dict(claim) await self.on_transaction_dict(claim)
return claim return claim
async def publish(self, name, *args, confirm=True, **kwargs):
claim = await self.out(self.daemon.jsonrpc_publish(name, *args, **kwargs))
self.assertEqual(claim['outputs'][0]['name'], name)
if confirm:
await self.on_transaction_dict(claim)
await self.generate(1)
await self.on_transaction_dict(claim)
return claim
async def channel_create(self, name='@arena', bid='1.0', confirm=True, **kwargs): async def channel_create(self, name='@arena', bid='1.0', confirm=True, **kwargs):
channel = await self.out(self.daemon.jsonrpc_channel_create(name, bid, **kwargs)) channel = await self.out(self.daemon.jsonrpc_channel_create(name, bid, **kwargs))
self.assertEqual(channel['outputs'][0]['name'], name) self.assertEqual(channel['outputs'][0]['name'], name)