Skip to content

Commit e025433

Browse files
authored
Merge pull request #86 from MerginMaps/checking_project_ids
Added logic to use project ID to check that the project is the same.
2 parents f2fb28e + fee04ef commit e025433

File tree

2 files changed

+86
-12
lines changed

2 files changed

+86
-12
lines changed

dbsync.py

Lines changed: 62 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import sys
1616
import tempfile
1717
import random
18+
import uuid
1819

1920
import psycopg2
2021
from itertools import chain
@@ -199,14 +200,25 @@ def _get_project_version(work_path):
199200
return mp.metadata["version"]
200201

201202

202-
def _set_db_project_comment(conn, schema, project_name, version, error=None):
203+
def _get_project_id(mp):
204+
""" Returns the project ID """
205+
try:
206+
project_id = uuid.UUID(mp.metadata["project_id"])
207+
except (KeyError, ValueError):
208+
project_id = None
209+
return project_id
210+
211+
212+
def _set_db_project_comment(conn, schema, project_name, version, project_id=None, error=None):
203213
""" Set postgres COMMENT on SCHEMA with Mergin Maps project name and version
204214
or eventually error message if initialisation failed
205215
"""
206216
comment = {
207217
"name": project_name,
208218
"version": version,
209219
}
220+
if project_id:
221+
comment["project_id"] = project_id
210222
if error:
211223
comment["error"] = error
212224
cur = conn.cursor()
@@ -238,6 +250,25 @@ def _redownload_project(conn_cfg, mc, work_dir, db_proj_info):
238250
raise DbSyncError("Mergin Maps client error: " + str(e))
239251

240252

253+
def _validate_local_project_id(mp, mc, server_info=None):
254+
"""Compare local project ID with remote version on the server."""
255+
local_project_id = _get_project_id(mp)
256+
if local_project_id is None:
257+
return
258+
project_path = mp.metadata["name"]
259+
if server_info is None:
260+
try:
261+
server_info = mc.project_info(project_path)
262+
except ClientError as e:
263+
raise DbSyncError("Mergin Maps client error: " + str(e))
264+
265+
remote_project_id = uuid.UUID(server_info["id"])
266+
if local_project_id != remote_project_id:
267+
raise DbSyncError(
268+
f"The local project ID ({local_project_id}) does not match the server project ID ({remote_project_id})"
269+
)
270+
271+
241272
def create_mergin_client():
242273
""" Create instance of MerginClient"""
243274
_check_has_password()
@@ -301,6 +332,10 @@ def pull(conn_cfg, mc):
301332
mp.set_tables_to_skip(ignored_tables)
302333
if mp.geodiff is None:
303334
raise DbSyncError("Mergin Maps client installation problem: geodiff not available")
335+
336+
# Make sure that local project ID (if available) is the same as on the server
337+
_validate_local_project_id(mp, mc)
338+
304339
project_path = mp.metadata["name"]
305340
local_version = mp.metadata["version"]
306341

@@ -390,25 +425,26 @@ def status(conn_cfg, mc):
390425
mp.set_tables_to_skip(ignored_tables)
391426
if mp.geodiff is None:
392427
raise DbSyncError("Mergin Maps client installation problem: geodiff not available")
393-
status_push = mp.get_push_changes()
394-
if status_push['added'] or status_push['updated'] or status_push['removed']:
395-
raise DbSyncError("Pending changes in the local directory - that should never happen! " + str(status_push))
396-
397428
project_path = mp.metadata["name"]
398429
local_version = mp.metadata["version"]
399-
print("Working directory " + work_dir)
400-
print("Mergin Maps project " + project_path + " at local version " + local_version)
401-
print("")
402430
print("Checking status...")
403-
404-
# check if there are any pending changes on server
405431
try:
406432
server_info = mc.project_info(project_path, since=local_version)
407433
except ClientError as e:
408434
raise DbSyncError("Mergin Maps client error: " + str(e))
409435

410-
print("Server is at version " + server_info["version"])
436+
# Make sure that local project ID (if available) is the same as on the server
437+
_validate_local_project_id(mp, mc, server_info)
438+
439+
status_push = mp.get_push_changes()
440+
if status_push['added'] or status_push['updated'] or status_push['removed']:
441+
raise DbSyncError("Pending changes in the local directory - that should never happen! " + str(status_push))
411442

443+
print("Working directory " + work_dir)
444+
print("Mergin Maps project " + project_path + " at local version " + local_version)
445+
print("")
446+
447+
print("Server is at version " + server_info["version"])
412448
status_pull = mp.get_pull_changes(server_info["files"])
413449
if status_pull['added'] or status_pull['updated'] or status_pull['removed']:
414450
print("There are pending changes on server:")
@@ -462,6 +498,10 @@ def push(conn_cfg, mc):
462498
mp.set_tables_to_skip(ignored_tables)
463499
if mp.geodiff is None:
464500
raise DbSyncError("Mergin Maps client installation problem: geodiff not available")
501+
502+
# Make sure that local project ID (if available) is the same as on the server
503+
_validate_local_project_id(mp, mc)
504+
465505
project_path = mp.metadata["name"]
466506
local_version = mp.metadata["version"]
467507

@@ -560,9 +600,17 @@ def init(conn_cfg, mc, from_gpkg=True):
560600
f"to {work_dir}")
561601
mc.download_project(conn_cfg.mergin_project, work_dir, db_proj_info["version"])
562602
else:
603+
# Get project ID from DB if available
563604
try:
564605
local_version = _get_project_version(work_dir)
565606
print(f"Working directory {work_dir} already exists, with project version {local_version}")
607+
# Compare local and database project version
608+
db_project_id_str = getattr(db_proj_info, "project_id", None)
609+
db_project_id = uuid.UUID(db_project_id_str) if db_project_id_str else None
610+
mp = _get_mergin_project(work_dir)
611+
local_project_id = _get_project_id(mp)
612+
if (db_project_id and local_project_id) and (db_project_id != local_project_id):
613+
raise DbSyncError(f"Database project ID doesn't match local project ID.")
566614
if local_version != db_proj_info["version"]:
567615
_redownload_project(conn_cfg, mc, work_dir, db_proj_info)
568616
except InvalidProject as e:
@@ -579,6 +627,9 @@ def init(conn_cfg, mc, from_gpkg=True):
579627
# make sure we have working directory now
580628
_check_has_working_dir(work_dir)
581629
local_version = _get_project_version(work_dir)
630+
mp = _get_mergin_project(work_dir)
631+
# Make sure that local project ID (if available) is the same as on the server
632+
_validate_local_project_id(mp, mc)
582633

583634
# check there are no pending changes on server (or locally - which should never happen)
584635
status_pull, status_push, _ = mc.project_status(work_dir)

test/test_basic.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import uuid
12

23
import pytest
34
import os
@@ -9,7 +10,7 @@
910

1011
from mergin import MerginClient, ClientError
1112
from dbsync import dbsync_init, dbsync_pull, dbsync_push, dbsync_status, config, DbSyncError, _geodiff_make_copy, \
12-
_get_db_project_comment, _get_mergin_project, config
13+
_get_db_project_comment, _get_mergin_project, _get_project_id, _validate_local_project_id, config
1314

1415
GEODIFF_EXE = os.environ.get('TEST_GEODIFF_EXE')
1516
DB_CONNINFO = os.environ.get('TEST_DB_CONNINFO')
@@ -409,3 +410,25 @@ def test_with_local_changes(mc):
409410
local_changes = mp.get_push_changes()
410411
assert any(local_changes.values()) is False
411412
dbsync_status(mc)
413+
414+
415+
def test_recreated_project_ids(mc):
416+
project_name = 'test_recreated_project_ids'
417+
source_gpkg_path = os.path.join(TEST_DATA_DIR, 'base.gpkg')
418+
project_dir = os.path.join(TMP_DIR, project_name + '_work') # working directory
419+
full_project_name = API_USER + "/" + project_name
420+
init_sync_from_geopackage(mc, project_name, source_gpkg_path)
421+
# delete remote project
422+
mc.delete_project(full_project_name)
423+
# recreate project with the same name
424+
mc.create_project(project_name)
425+
# comparing project IDs after recreating it with the same name
426+
mp = _get_mergin_project(project_dir)
427+
local_project_id = _get_project_id(mp)
428+
server_info = mc.project_info(full_project_name)
429+
server_project_id = uuid.UUID(server_info["id"])
430+
assert local_project_id is not None
431+
assert server_project_id is not None
432+
assert local_project_id != server_project_id
433+
with pytest.raises(DbSyncError):
434+
dbsync_status(mc)

0 commit comments

Comments
 (0)