mirror of
https://github.com/samsonjs/vdirsyncer.git
synced 2026-03-29 09:35:50 +00:00
Ability to sync differently named collections with each other (#423)
* Ability to sync differently named collections * Fixes * Fixes * Add example
This commit is contained in:
parent
777eb35898
commit
be8df955e9
6 changed files with 94 additions and 20 deletions
|
|
@ -16,6 +16,8 @@ Version 0.10.0
|
|||
have been added.
|
||||
- New global command line option `--config`, to specify an alternative config
|
||||
file. See :gh:`409`.
|
||||
- The ``collections`` parameter can now be used to synchronize
|
||||
differently-named collections with each other.
|
||||
|
||||
Version 0.9.3
|
||||
=============
|
||||
|
|
|
|||
|
|
@ -64,6 +64,12 @@ Pair Section
|
|||
The special values ``"from a"`` and ``"from b"``, tell vdirsyncer to try
|
||||
autodiscovery on a specific storage.
|
||||
|
||||
If the collection you want to sync doesn't have the same name on each side,
|
||||
you may also use a value of the form ``["config_name", "name_a", "name_b"]``.
|
||||
This will synchronize the collection ``name_a`` on side A with the collection
|
||||
``name_b`` on side B. The ``config_name`` will be used for representation in
|
||||
CLI arguments and logging.
|
||||
|
||||
Examples:
|
||||
|
||||
- ``collections = ["from b", "foo", "bar"]`` makes vdirsyncer synchronize the
|
||||
|
|
@ -72,6 +78,10 @@ Pair Section
|
|||
- ``collections = ["from b", from a"]`` makes vdirsyncer synchronize all
|
||||
existing collections on either side.
|
||||
|
||||
- ``collections = [["bar", "bar_a", "bar_b"], "foo"]`` makes vdirsyncer
|
||||
synchronize ``bar_a`` from side A with ``bar_b`` from side B, and also
|
||||
synchronize ``foo`` on both sides with each other.
|
||||
|
||||
- ``conflict_resolution``: Optional, define how conflicts should be handled. A
|
||||
conflict occurs when one item (event, task) changed on both sides since the
|
||||
last sync.
|
||||
|
|
|
|||
|
|
@ -199,7 +199,4 @@ def test_invalid_collections_arg():
|
|||
with pytest.raises(exceptions.UserError) as excinfo:
|
||||
_read_config(f)
|
||||
|
||||
assert (
|
||||
'Section `pair foobar`: `collections` parameter must be a list of '
|
||||
'collection names (strings!) or `null`.'
|
||||
) in str(excinfo.value)
|
||||
assert 'Expected string' in str(excinfo.value)
|
||||
|
|
|
|||
|
|
@ -81,3 +81,48 @@ def test_discover_on_unsupported_storage(tmpdir, runner):
|
|||
result = runner.invoke(['discover'])
|
||||
assert result.exception
|
||||
assert 'doesn\'t support collection discovery' in result.output
|
||||
|
||||
|
||||
def test_discover_different_collection_names(tmpdir, runner):
|
||||
foo = tmpdir.mkdir('foo')
|
||||
bar = tmpdir.mkdir('bar')
|
||||
runner.write_with_general(dedent('''
|
||||
[storage foo]
|
||||
type = filesystem
|
||||
fileext = .txt
|
||||
path = {foo}
|
||||
|
||||
[storage bar]
|
||||
type = filesystem
|
||||
fileext = .txt
|
||||
path = {bar}
|
||||
|
||||
[pair foobar]
|
||||
a = foo
|
||||
b = bar
|
||||
collections = [
|
||||
["coll1", "coll_a1", "coll_b1"],
|
||||
"coll2"
|
||||
]
|
||||
''').format(foo=str(foo), bar=str(bar)))
|
||||
|
||||
result = runner.invoke(['discover'], input='y\n' * 6)
|
||||
assert not result.exception
|
||||
|
||||
coll_a1 = foo.join('coll_a1')
|
||||
coll_b1 = bar.join('coll_b1')
|
||||
|
||||
assert coll_a1.exists()
|
||||
assert coll_b1.exists()
|
||||
|
||||
result = runner.invoke(['sync'])
|
||||
assert not result.exception
|
||||
|
||||
foo_txt = coll_a1.join('foo.txt')
|
||||
foo_txt.write('BEGIN:VCALENDAR\nUID:foo\nEND:VCALENDAR')
|
||||
|
||||
result = runner.invoke(['sync'])
|
||||
assert not result.exception
|
||||
|
||||
assert foo_txt.exists()
|
||||
assert coll_b1.join('foo.txt').exists()
|
||||
|
|
|
|||
|
|
@ -66,17 +66,26 @@ def _validate_pair_section(pair_config):
|
|||
if collections is None:
|
||||
return
|
||||
|
||||
e = ValueError('`collections` parameter must be a list of collection '
|
||||
'names (strings!) or `null`.')
|
||||
|
||||
if not isinstance(collections, list):
|
||||
raise e
|
||||
raise ValueError('`collections` parameter must be a list or `null`.')
|
||||
|
||||
if any(not isinstance(x, (text_type, bytes)) for x in collections):
|
||||
raise e
|
||||
collection_names = set()
|
||||
|
||||
if len(set(collections)) != len(collections):
|
||||
raise ValueError('Duplicate values in collections parameter.')
|
||||
for i, collection in enumerate(collections):
|
||||
if isinstance(collection, (text_type, bytes)):
|
||||
collection_name = collection
|
||||
elif isinstance(collection, list) and \
|
||||
len(collection) == 3 and \
|
||||
all(isinstance(x, (text_type, bytes)) for x in collection):
|
||||
collection_name = collection[0]
|
||||
else:
|
||||
raise ValueError('`collections` parameter, position {i}:'
|
||||
'Expected string or list of three strings.'
|
||||
.format(i=i))
|
||||
|
||||
if collection_name in collection_names:
|
||||
raise ValueError('Duplicate values in collections parameter.')
|
||||
collection_names.add(collection_name)
|
||||
|
||||
|
||||
def load_config(fname=None):
|
||||
|
|
|
|||
|
|
@ -276,7 +276,7 @@ def _collections_for_pair_impl(status_path, pair):
|
|||
a_discovered = _discover_from_config(pair.config_a)
|
||||
b_discovered = _discover_from_config(pair.config_b)
|
||||
|
||||
for shortcut in set(shortcuts):
|
||||
for shortcut in shortcuts:
|
||||
if shortcut == 'from a':
|
||||
collections = a_discovered
|
||||
elif shortcut == 'from b':
|
||||
|
|
@ -285,17 +285,28 @@ def _collections_for_pair_impl(status_path, pair):
|
|||
collections = [shortcut]
|
||||
|
||||
for collection in collections:
|
||||
try:
|
||||
a_args = a_discovered[collection]
|
||||
except KeyError:
|
||||
a_args = _handle_collection_not_found(pair.config_a,
|
||||
collection)
|
||||
if isinstance(collection, list):
|
||||
try:
|
||||
collection, collection_a, collection_b = collection
|
||||
except ValueError:
|
||||
raise exceptions.UserError(
|
||||
'Expected string or list of length 3, '
|
||||
'{} found instead.'
|
||||
.format(collection))
|
||||
else:
|
||||
collection_a = collection_b = collection
|
||||
|
||||
try:
|
||||
b_args = b_discovered[collection]
|
||||
a_args = a_discovered[collection_a]
|
||||
except KeyError:
|
||||
a_args = _handle_collection_not_found(pair.config_a,
|
||||
collection_a)
|
||||
|
||||
try:
|
||||
b_args = b_discovered[collection_b]
|
||||
except KeyError:
|
||||
b_args = _handle_collection_not_found(pair.config_b,
|
||||
collection)
|
||||
collection_b)
|
||||
|
||||
yield collection, (a_args, b_args)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue