@saltcorn/cli 0.9.0-beta.6 → 0.9.0-beta.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":"0.9.0-beta.6","commands":{"add-schema":{"id":"add-schema","description":"Add Saltcorn schema to existing database","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"force":{"name":"force","type":"boolean","char":"f","description":"force command execution","allowNo":false}},"args":[]},"backup":{"id":"backup","description":"Backup the PostgreSQL database to a file with pg_dump or saltcorn backup zip","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"verbose":{"name":"verbose","type":"boolean","char":"v","description":"Verbose","allowNo":false},"output":{"name":"output","type":"option","char":"o","description":"output filename"},"tenant":{"name":"tenant","type":"option","char":"t","description":"Backup tenant in saltcorn zip format"},"zip":{"name":"zip","type":"boolean","char":"z","description":"Backup public in saltcorn zip format","allowNo":false}},"args":[]},"build-app":{"id":"build-app","description":"Build mobile app","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"tenantAppName":{"name":"tenantAppName","type":"option","description":"Optional name of a tenant application, if set, the app will be build for this tenant"},"platforms":{"name":"platforms","type":"option","char":"p","description":"Platforms to build for, space separated list"},"entryPoint":{"name":"entryPoint","type":"option","char":"e","description":"This is the first view or page (see -t) after the login."},"entryPointType":{"name":"entryPointType","type":"option","char":"t","description":"Type of the entry point ('view' or 'page'). The default is 'view'."},"localUserTables":{"name":"localUserTables","type":"option","char":"l","description":"user defined tables that should be replicated into the app"},"synchedTables":{"name":"synchedTables","type":"option","description":"Table names for which the offline should be synchronized with the saltcorn server"},"includedPlugins":{"name":"includedPlugins","type":"option","description":"Names of plugins that should be bundled into the app.If empty, no modules are used."},"useDocker":{"name":"useDocker","type":"boolean","char":"d","description":"Use a docker container to build the app.","allowNo":false},"buildDirectory":{"name":"buildDirectory","type":"option","char":"b","description":"A directory where the app should be build"},"copyAppDirectory":{"name":"copyAppDirectory","type":"option","char":"c","description":"If set, the app file will be copied here, please set 'user email', too"},"userEmail":{"name":"userEmail","type":"option","char":"u","description":"Email of the user building the app"},"appName":{"name":"appName","type":"option","description":"Name of the mobile app (default SaltcornMobileApp)"},"appVersion":{"name":"appVersion","type":"option","description":"Version of the mobile app (default 1.0.0)"},"appIcon":{"name":"appIcon","type":"option","description":"A png that will be used as launcher icon. The default is a png of a saltcorn symbol."},"serverURL":{"name":"serverURL","type":"option","char":"s","description":"URL to a saltcorn server"},"splashPage":{"name":"splashPage","type":"option","description":"Name of a page that should be shown while the app is loading."},"autoPublicLogin":{"name":"autoPublicLogin","type":"boolean","description":"Show public entry points before the login as a public user.","allowNo":false},"allowOfflineMode":{"name":"allowOfflineMode","type":"boolean","description":"Switch to offline mode when there is no internet, sync the data when a connection is available again.","allowNo":false},"buildForEmulator":{"name":"buildForEmulator","type":"boolean","description":"build without '--device', generates no .ipa file so that iOS apps can be build without developer accounts","allowNo":false}},"args":[]},"build-cordova-builder":{"id":"build-cordova-builder","description":"Build the 'saltcorn/cordova-builder' docker image","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"buildClean":{"name":"buildClean","type":"boolean","description":"run a clean build with --no-cache","allowNo":false}},"args":[]},"configuration-check-backups":{"id":"configuration-check-backups","description":"Check configuration","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"destructive":{"name":"destructive","type":"boolean","char":"d","description":"destructive","allowNo":false}},"args":[{"name":"files","description":"backup file to check. can be repeated, e.g. with *","required":true}]},"configuration-check":{"id":"configuration-check","description":"Check configuration","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"tenant":{"name":"tenant","type":"option","char":"t","description":"tenant"}},"args":[]},"create-tenant":{"id":"create-tenant","description":"Create a tenant","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"url":{"name":"url","type":"option","description":"Url of tenant"},"email":{"name":"email","type":"option","char":"e","description":"Email of owner of tenant"},"description":{"name":"description","type":"option","char":"d","description":"Description of tenant"}},"args":[{"name":"tenant","description":"Tenant subdomain to create","required":true}]},"create-user":{"id":"create-user","description":"Create a new user","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"admin":{"name":"admin","type":"boolean","char":"a","description":"Admin user","allowNo":false},"tenant":{"name":"tenant","type":"option","char":"t","description":"tenant"},"email":{"name":"email","type":"option","char":"e","description":"email"},"role":{"name":"role","type":"option","char":"r","description":"role"},"password":{"name":"password","type":"option","char":"p","description":"password"}},"args":[]},"delete-tenants":{"id":"delete-tenants","description":"Delete inactive tenants","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{},"args":[]},"delete-user":{"id":"delete-user","description":"Delete user.\n\nCommand deletes the user specified by USER_EMAIL.\n\n","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"force":{"name":"force","type":"boolean","char":"f","description":"force command execution","allowNo":false},"tenant":{"name":"tenant","type":"option","char":"t","description":"tenant"}},"args":[{"name":"user_email","description":"User to delete","required":true}]},"fixtures":{"id":"fixtures","description":"Load fixtures for testing\n...\nThis manual step it is never required for users and rarely required for developers\n","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"reset":{"name":"reset","type":"boolean","char":"r","description":"Also reset schema","allowNo":false},"tenant":{"name":"tenant","type":"option","char":"t","description":"tenant"}},"args":[]},"get-cfg":{"id":"get-cfg","description":"Get a configuration value. The value is printed to stdout as a JSON value","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"tenant":{"name":"tenant","type":"option","char":"t","description":"tenant"},"plugin":{"name":"plugin","type":"option","char":"p","description":"plugin"}},"args":[{"name":"key","description":"Configuration key","required":false}]},"info":{"id":"info","description":"Show paths\n...\nShow configuration and file store paths\n","pluginName":"@saltcorn/cli","pluginType":"core","aliases":["paths"],"flags":{"json":{"name":"json","type":"boolean","char":"j","description":"json format","allowNo":false}},"args":[]},"inspect":{"id":"inspect","description":"Inspect an entity's JSON representation, or list entities","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"tenant":{"name":"tenant","type":"option","char":"t","description":"tenant"}},"args":[{"name":"type","description":"Entity type","required":true,"options":["view","page","trigger","table"]},{"name":"name","description":"Entity name. If not supplied, list all names"}]},"install-pack":{"id":"install-pack","description":"Install a pack or restore a snapshot","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"tenant":{"name":"tenant","type":"option","char":"t","description":"tenant"},"name":{"name":"name","type":"option","char":"n","description":"Pack name in store"},"file":{"name":"file","type":"option","char":"f","description":"File with pack JSON"}},"args":[]},"install-plugin":{"id":"install-plugin","description":"Install a plugin","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"tenant":{"name":"tenant","type":"option","char":"t","description":"tenant"},"name":{"name":"name","type":"option","char":"n","description":"Plugin name in store"},"directory":{"name":"directory","type":"option","char":"d","description":"Directory with local plugin"}},"args":[]},"list-tenants":{"id":"list-tenants","description":"List tenants in CSV format","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"tenant":{"name":"tenant","type":"option","char":"t","description":"tenant","required":false},"verbose":{"name":"verbose","type":"boolean","char":"v","description":"verbose output","required":false,"allowNo":false},"json":{"name":"json","type":"boolean","char":"j","description":"json format","allowNo":false}},"args":[]},"list-triggers":{"id":"list-triggers","description":"List triggers","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"tenant":{"name":"tenant","type":"option","char":"t","description":"tenant","required":false},"verbose":{"name":"verbose","type":"boolean","char":"v","description":"verbose output","required":false,"allowNo":false},"json":{"name":"json","type":"boolean","char":"j","description":"json format","allowNo":false}},"args":[]},"list-users":{"id":"list-users","description":"List users","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"tenant":{"name":"tenant","type":"option","char":"t","description":"tenant","required":false},"verbose":{"name":"verbose","type":"boolean","char":"v","description":"verbose output","required":false,"allowNo":false}},"args":[]},"migrate":{"id":"migrate","description":"Run Database structure migrations\n...\nNOTE!\n- Please stop Saltcorn before run DB migrations.\n- Please make db backup before migration.\n- There are no way to rollback migration if you doesn't make backup.\n\nThis is not normally required as migrations will be run when the server starts.\nHowever, this command may be useful if you are running multiple application\nservers and need to control when the migrations are run.\n","usage":"saltcorn migrate","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{},"args":[]},"modify-user":{"id":"modify-user","description":"Modify (update) user.\n\nCommand changes the user specified by USER_EMAIL.\n\nYou can change the user group, password and email.\n\nNOTE that -a and -r role (--role=role) can give conflict.\n","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"admin":{"name":"admin","type":"boolean","char":"a","description":"make user be Admin","allowNo":false},"tenant":{"name":"tenant","type":"option","char":"t","description":"tenant"},"email":{"name":"email","type":"option","char":"e","description":"new email"},"role":{"name":"role","type":"option","char":"r","description":"new role (can conflict with -a option)"},"password":{"name":"password","type":"option","char":"p","description":"new password"},"imode":{"name":"imode","type":"boolean","char":"i","description":"interactive mode","allowNo":false}},"args":[{"name":"user_email","description":"User to modify","required":true}]},"plugins":{"id":"plugins","description":"List and upgrade plugins for tenants\n...\nExtra documentation goes here\n","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"examples":["plugins -v - verbose output of commands","plugins -u -d - dry-run for plugin update","plugins -u -f - force plugin update"],"flags":{"upgrade":{"name":"upgrade","type":"boolean","char":"u","description":"Upgrade","allowNo":false},"dryRun":{"name":"dryRun","type":"boolean","char":"d","description":"Upgrade dry-run","allowNo":false},"verbose":{"name":"verbose","type":"boolean","char":"v","description":"Verbose output","allowNo":false},"force":{"name":"force","type":"boolean","char":"f","description":"Force update","allowNo":false},"name":{"name":"name","type":"option","char":"n","description":"Plugin name"}},"args":[]},"reset-schema":{"id":"reset-schema","description":"Reset the database\n...\nThis will delete all existing information\n","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"force":{"name":"force","type":"boolean","char":"f","description":"force command execution","allowNo":false},"tenant":{"name":"tenant","type":"option","char":"t","description":"tenant"}},"args":[]},"restore":{"id":"restore","description":"Restore a previously backed up database (zip or sqlc format)","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"tenant":{"name":"tenant","type":"option","char":"t","description":"tenant"}},"args":[{"name":"file","description":"backup file to restore","required":true}]},"rm-tenant":{"id":"rm-tenant","description":"Remove a tenant.\nAttention! All tenant data will be lost!\nIt recommended to make backup of tenant before perform this command.\n","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"force":{"name":"force","type":"boolean","char":"f","description":"force command execution","allowNo":false},"tenant":{"name":"tenant","type":"option","char":"t","description":"tenant","required":true}},"args":[]},"run-benchmark":{"id":"run-benchmark","description":"Run benchmark","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"token":{"name":"token","type":"option","char":"t","description":"API Token for reporting results"},"benchmark":{"name":"benchmark","type":"option","char":"b","description":"Which benchmark to run"},"delay":{"name":"delay","type":"option","char":"d","description":"delay between runs (s)","default":30}},"args":[{"name":"baseurl","description":"Base URL","required":false}]},"run-js":{"id":"run-js","description":"Run javascript code","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"tenant":{"name":"tenant","type":"option","char":"t","description":"tenant name"},"code":{"name":"code","type":"option","char":"c","description":"js code"},"file":{"name":"file","type":"option","char":"f","description":"path to script file"}},"args":[]},"run-sql":{"id":"run-sql","description":"Run sql expression","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"tenant":{"name":"tenant","type":"option","char":"t","description":"tenant name"},"sql":{"name":"sql","type":"option","char":"s","description":"sql statement"},"file":{"name":"file","type":"option","char":"f","description":"path to sql file name"}},"args":[]},"run-tests":{"id":"run-tests","description":"Run test suites","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"coverage":{"name":"coverage","type":"boolean","char":"c","description":"Coverage","allowNo":false},"listTests":{"name":"listTests","type":"boolean","char":"l","description":"List tests","allowNo":false},"verbose":{"name":"verbose","type":"boolean","char":"v","description":"Verbose","allowNo":false},"detectOpenHandles":{"name":"detectOpenHandles","type":"boolean","char":"d","description":"Detect Open Handles","allowNo":false},"testFilter":{"name":"testFilter","type":"option","char":"t","description":"Filter tests by suite or test name"},"watch":{"name":"watch","type":"boolean","description":"Watch files for changes and rerun tests related to changed files.","allowNo":false},"watchAll":{"name":"watchAll","type":"boolean","description":"Watch files for changes and rerun all tests.","allowNo":false},"database":{"name":"database","type":"option","description":"Run on specified database. Default is saltcorn_test"}},"args":[{"name":"package","description":"which package to run tests for"}]},"run-trigger":{"id":"run-trigger","description":"Run a trigger","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"tenant":{"name":"tenant","type":"option","char":"t","description":"tenant","required":false}},"args":[{"name":"trigger","description":"trigger name","required":true}]},"scheduler":{"id":"scheduler","description":"Run the Saltcorn scheduler","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"verbose":{"name":"verbose","type":"boolean","char":"v","description":"Verbose","allowNo":false}},"args":[]},"serve":{"id":"serve","description":"Start the Saltcorn server","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"port":{"name":"port","type":"option","char":"p","description":"port","default":3000},"verbose":{"name":"verbose","type":"boolean","char":"v","description":"Verbose","allowNo":false},"watchReaper":{"name":"watchReaper","type":"boolean","char":"r","description":"Watch reaper","allowNo":false},"dev":{"name":"dev","type":"boolean","char":"d","description":"Run in dev mode and re-start on file changes","allowNo":false},"addschema":{"name":"addschema","type":"boolean","char":"a","description":"Add schema if missing","allowNo":false},"nomigrate":{"name":"nomigrate","type":"boolean","char":"n","description":"No migrations","allowNo":false},"noscheduler":{"name":"noscheduler","type":"boolean","char":"s","description":"No scheduler","allowNo":false},"subdomain_offset":{"name":"subdomain_offset","type":"option","description":"Number of parts to remove to access subdomain in 'multi_tenant' mode"}},"args":[]},"set-cfg":{"id":"set-cfg","description":"Set a configuration value. The supplied value (argument, or file stdin) will be parsed as JSON. If this fails, it is stored as a string.","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"tenant":{"name":"tenant","type":"option","char":"t","description":"tenant"},"plugin":{"name":"plugin","type":"option","char":"p","description":"plugin"},"file":{"name":"file","type":"option","char":"f","description":"file"},"stdin":{"name":"stdin","type":"boolean","char":"i","description":"read value from stdin","allowNo":false}},"args":[{"name":"key","description":"Configuration key","required":false},{"name":"value","description":"Configuration value (JSON or string)"}]},"setup-benchmark":{"id":"setup-benchmark","description":"Setup an instance for benchmarking","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"tenant":{"name":"tenant","type":"option","char":"t","description":"tenant"}},"args":[]},"setup":{"id":"setup","description":"Set up a new system\n...\nThis will attempt to install or connect a database, and set up a\nconfiguration file\n","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"coverage":{"name":"coverage","type":"boolean","char":"c","description":"Coverage","allowNo":false}},"args":[]},"sync-upload-data":{"id":"sync-upload-data","description":"Runs a sync for data supplied by the mobile app","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"tenantAppName":{"name":"tenantAppName","type":"option","description":"Optional name of a tenant application"},"userEmail":{"name":"userEmail","type":"option","description":"email of the user running the sync"},"directory":{"name":"directory","type":"option","description":"directory name for input output data"},"syncTimestamp":{"name":"syncTimestamp","type":"option","description":"new timestamp for the sync_info rows"}},"args":[]},"take-snapshot":{"id":"take-snapshot","description":"Print a current snapshout to stdout","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"tenant":{"name":"tenant","type":"option","char":"t","description":"tenant"},"fresh":{"name":"fresh","type":"boolean","char":"f","description":"fresh","allowNo":false}},"args":[]},"transform-field":{"id":"transform-field","description":"transform an existing field by applying a calculated expression","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"expression","description":"expression to calculate field","required":true},{"name":"field","description":"field name","required":true},{"name":"table","description":"table name","required":true},{"name":"tenant","description":"tenant name","required":false}]},"dev:localize-plugin":{"id":"dev:localize-plugin","description":"Convert plugin to local plugin","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"unlocalize":{"name":"unlocalize","type":"boolean","char":"u","description":"Unlocalize plugin (local to npm)","allowNo":false},"tenant":{"name":"tenant","type":"option","char":"t","description":"tenant"}},"args":[{"name":"plugin","description":"Current plugin name","required":true},{"name":"path","description":"Absolute path to local plugin"}]},"dev:make-migration":{"id":"dev:make-migration","description":"Create a new blank Database structure migration file.\nThese migrations update database structure.\nYou should not normally need to run this\nunless you are a developer.\n","usage":"make-migration","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{},"args":[]},"dev:plugin-test":{"id":"dev:plugin-test","description":"Install a plugin, spawn 'npm run test' in the install directory and check the return code.","usage":"saltcorn dev:plugin-test -d [PATH_TO_LOCAL_PLUGIN]/statistics -f test-backup.zip","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"directory":{"name":"directory","type":"option","char":"d","description":"Directory of local plugin"},"name":{"name":"name","type":"option","char":"n","description":"Plugin name in store of a released plugin"},"backupFile":{"name":"backupFile","type":"option","char":"f","description":"Optional name of a backup file in the tests folder. If you ommit this, then the test has to create its own data."},"database":{"name":"database","type":"option","description":"Run on specified database. Default is 'saltcorn_test''"}},"args":[]},"dev:post-release":{"id":"dev:post-release","description":"Post-release tasks: docker and vagrant builds","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"task","description":"What to do","options":["docker","vagrant","all","none"]}]},"dev:release":{"id":"dev:release","description":"Release a new saltcorn version","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"version","description":"New version number","required":true}]},"dev:test-plugin":{"id":"dev:test-plugin","description":"Test a plugin\n...\nExtra documentation goes here\n","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"path","description":"path to plugin package","required":true}]}}}
1
+ {"version":"0.9.0-beta.8","commands":{"add-schema":{"id":"add-schema","description":"Add Saltcorn schema to existing database","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"force":{"name":"force","type":"boolean","char":"f","description":"force command execution","allowNo":false}},"args":[]},"backup":{"id":"backup","description":"Backup the PostgreSQL database to a file with pg_dump or saltcorn backup zip","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"verbose":{"name":"verbose","type":"boolean","char":"v","description":"Verbose","allowNo":false},"output":{"name":"output","type":"option","char":"o","description":"output filename"},"tenant":{"name":"tenant","type":"option","char":"t","description":"Backup tenant in saltcorn zip format"},"zip":{"name":"zip","type":"boolean","char":"z","description":"Backup public in saltcorn zip format","allowNo":false}},"args":[]},"build-app":{"id":"build-app","description":"Build mobile app","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"tenantAppName":{"name":"tenantAppName","type":"option","description":"Optional name of a tenant application, if set, the app will be build for this tenant"},"platforms":{"name":"platforms","type":"option","char":"p","description":"Platforms to build for, space separated list"},"entryPoint":{"name":"entryPoint","type":"option","char":"e","description":"This is the first view or page (see -t) after the login."},"entryPointType":{"name":"entryPointType","type":"option","char":"t","description":"Type of the entry point ('view' or 'page'). The default is 'view'."},"localUserTables":{"name":"localUserTables","type":"option","char":"l","description":"user defined tables that should be replicated into the app"},"synchedTables":{"name":"synchedTables","type":"option","description":"Table names for which the offline should be synchronized with the saltcorn server"},"includedPlugins":{"name":"includedPlugins","type":"option","description":"Names of plugins that should be bundled into the app.If empty, no modules are used."},"useDocker":{"name":"useDocker","type":"boolean","char":"d","description":"Use a docker container to build the app.","allowNo":false},"buildDirectory":{"name":"buildDirectory","type":"option","char":"b","description":"A directory where the app should be build"},"copyAppDirectory":{"name":"copyAppDirectory","type":"option","char":"c","description":"If set, the app file will be copied here, please set 'user email', too"},"userEmail":{"name":"userEmail","type":"option","char":"u","description":"Email of the user building the app"},"appName":{"name":"appName","type":"option","description":"Name of the mobile app (default SaltcornMobileApp)"},"appVersion":{"name":"appVersion","type":"option","description":"Version of the mobile app (default 1.0.0)"},"appIcon":{"name":"appIcon","type":"option","description":"A png that will be used as launcher icon. The default is a png of a saltcorn symbol."},"serverURL":{"name":"serverURL","type":"option","char":"s","description":"URL to a saltcorn server"},"splashPage":{"name":"splashPage","type":"option","description":"Name of a page that should be shown while the app is loading."},"autoPublicLogin":{"name":"autoPublicLogin","type":"boolean","description":"Show public entry points before the login as a public user.","allowNo":false},"allowOfflineMode":{"name":"allowOfflineMode","type":"boolean","description":"Switch to offline mode when there is no internet, sync the data when a connection is available again.","allowNo":false},"buildForEmulator":{"name":"buildForEmulator","type":"boolean","description":"build without '--device', generates no .ipa file so that iOS apps can be build without developer accounts","allowNo":false}},"args":[]},"build-cordova-builder":{"id":"build-cordova-builder","description":"Build the 'saltcorn/cordova-builder' docker image","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"buildClean":{"name":"buildClean","type":"boolean","description":"run a clean build with --no-cache","allowNo":false}},"args":[]},"configuration-check-backups":{"id":"configuration-check-backups","description":"Check configuration","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"destructive":{"name":"destructive","type":"boolean","char":"d","description":"destructive","allowNo":false}},"args":[{"name":"files","description":"backup file to check. can be repeated, e.g. with *","required":true}]},"configuration-check":{"id":"configuration-check","description":"Check configuration","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"tenant":{"name":"tenant","type":"option","char":"t","description":"tenant"}},"args":[]},"create-tenant":{"id":"create-tenant","description":"Create a tenant","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"url":{"name":"url","type":"option","description":"Url of tenant"},"email":{"name":"email","type":"option","char":"e","description":"Email of owner of tenant"},"description":{"name":"description","type":"option","char":"d","description":"Description of tenant"}},"args":[{"name":"tenant","description":"Tenant subdomain to create","required":true}]},"create-user":{"id":"create-user","description":"Create a new user","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"admin":{"name":"admin","type":"boolean","char":"a","description":"Admin user","allowNo":false},"tenant":{"name":"tenant","type":"option","char":"t","description":"tenant"},"email":{"name":"email","type":"option","char":"e","description":"email"},"role":{"name":"role","type":"option","char":"r","description":"role"},"password":{"name":"password","type":"option","char":"p","description":"password"}},"args":[]},"delete-tenants":{"id":"delete-tenants","description":"Delete inactive tenants","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{},"args":[]},"delete-user":{"id":"delete-user","description":"Delete user.\n\nCommand deletes the user specified by USER_EMAIL.\n\n","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"force":{"name":"force","type":"boolean","char":"f","description":"force command execution","allowNo":false},"tenant":{"name":"tenant","type":"option","char":"t","description":"tenant"}},"args":[{"name":"user_email","description":"User to delete","required":true}]},"fixtures":{"id":"fixtures","description":"Load fixtures for testing\n...\nThis manual step it is never required for users and rarely required for developers\n","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"reset":{"name":"reset","type":"boolean","char":"r","description":"Also reset schema","allowNo":false},"tenant":{"name":"tenant","type":"option","char":"t","description":"tenant"}},"args":[]},"get-cfg":{"id":"get-cfg","description":"Get a configuration value. The value is printed to stdout as a JSON value","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"tenant":{"name":"tenant","type":"option","char":"t","description":"tenant"},"plugin":{"name":"plugin","type":"option","char":"p","description":"plugin"}},"args":[{"name":"key","description":"Configuration key","required":false}]},"info":{"id":"info","description":"Show paths\n...\nShow configuration and file store paths\n","pluginName":"@saltcorn/cli","pluginType":"core","aliases":["paths"],"flags":{"json":{"name":"json","type":"boolean","char":"j","description":"json format","allowNo":false}},"args":[]},"inspect":{"id":"inspect","description":"Inspect an entity's JSON representation, or list entities","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"tenant":{"name":"tenant","type":"option","char":"t","description":"tenant"}},"args":[{"name":"type","description":"Entity type","required":true,"options":["view","page","trigger","table"]},{"name":"name","description":"Entity name. If not supplied, list all names"}]},"install-pack":{"id":"install-pack","description":"Install a pack or restore a snapshot","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"tenant":{"name":"tenant","type":"option","char":"t","description":"tenant"},"name":{"name":"name","type":"option","char":"n","description":"Pack name in store"},"file":{"name":"file","type":"option","char":"f","description":"File with pack JSON"}},"args":[]},"install-plugin":{"id":"install-plugin","description":"Install a plugin","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"tenant":{"name":"tenant","type":"option","char":"t","description":"tenant"},"name":{"name":"name","type":"option","char":"n","description":"Plugin name in store"},"directory":{"name":"directory","type":"option","char":"d","description":"Directory with local plugin"}},"args":[]},"list-tenants":{"id":"list-tenants","description":"List tenants in CSV format","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"tenant":{"name":"tenant","type":"option","char":"t","description":"tenant","required":false},"verbose":{"name":"verbose","type":"boolean","char":"v","description":"verbose output","required":false,"allowNo":false},"json":{"name":"json","type":"boolean","char":"j","description":"json format","allowNo":false}},"args":[]},"list-triggers":{"id":"list-triggers","description":"List triggers","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"tenant":{"name":"tenant","type":"option","char":"t","description":"tenant","required":false},"verbose":{"name":"verbose","type":"boolean","char":"v","description":"verbose output","required":false,"allowNo":false},"json":{"name":"json","type":"boolean","char":"j","description":"json format","allowNo":false}},"args":[]},"list-users":{"id":"list-users","description":"List users","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"tenant":{"name":"tenant","type":"option","char":"t","description":"tenant","required":false},"verbose":{"name":"verbose","type":"boolean","char":"v","description":"verbose output","required":false,"allowNo":false}},"args":[]},"migrate":{"id":"migrate","description":"Run Database structure migrations\n...\nNOTE!\n- Please stop Saltcorn before run DB migrations.\n- Please make db backup before migration.\n- There are no way to rollback migration if you doesn't make backup.\n\nThis is not normally required as migrations will be run when the server starts.\nHowever, this command may be useful if you are running multiple application\nservers and need to control when the migrations are run.\n","usage":"saltcorn migrate","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{},"args":[]},"modify-user":{"id":"modify-user","description":"Modify (update) user.\n\nCommand changes the user specified by USER_EMAIL.\n\nYou can change the user group, password and email.\n\nNOTE that -a and -r role (--role=role) can give conflict.\n","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"admin":{"name":"admin","type":"boolean","char":"a","description":"make user be Admin","allowNo":false},"tenant":{"name":"tenant","type":"option","char":"t","description":"tenant"},"email":{"name":"email","type":"option","char":"e","description":"new email"},"role":{"name":"role","type":"option","char":"r","description":"new role (can conflict with -a option)"},"password":{"name":"password","type":"option","char":"p","description":"new password"},"imode":{"name":"imode","type":"boolean","char":"i","description":"interactive mode","allowNo":false}},"args":[{"name":"user_email","description":"User to modify","required":true}]},"plugins":{"id":"plugins","description":"List and upgrade plugins for tenants\n...\nExtra documentation goes here\n","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"examples":["plugins -v - verbose output of commands","plugins -u -d - dry-run for plugin update","plugins -u -f - force plugin update"],"flags":{"upgrade":{"name":"upgrade","type":"boolean","char":"u","description":"Upgrade","allowNo":false},"dryRun":{"name":"dryRun","type":"boolean","char":"d","description":"Upgrade dry-run","allowNo":false},"verbose":{"name":"verbose","type":"boolean","char":"v","description":"Verbose output","allowNo":false},"force":{"name":"force","type":"boolean","char":"f","description":"Force update","allowNo":false},"name":{"name":"name","type":"option","char":"n","description":"Plugin name"}},"args":[]},"reset-schema":{"id":"reset-schema","description":"Reset the database\n...\nThis will delete all existing information\n","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"force":{"name":"force","type":"boolean","char":"f","description":"force command execution","allowNo":false},"tenant":{"name":"tenant","type":"option","char":"t","description":"tenant"}},"args":[]},"restore":{"id":"restore","description":"Restore a previously backed up database (zip or sqlc format)","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"tenant":{"name":"tenant","type":"option","char":"t","description":"tenant"}},"args":[{"name":"file","description":"backup file to restore","required":true}]},"rm-tenant":{"id":"rm-tenant","description":"Remove a tenant.\nAttention! All tenant data will be lost!\nIt recommended to make backup of tenant before perform this command.\n","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"force":{"name":"force","type":"boolean","char":"f","description":"force command execution","allowNo":false},"tenant":{"name":"tenant","type":"option","char":"t","description":"tenant","required":true}},"args":[]},"run-benchmark":{"id":"run-benchmark","description":"Run benchmark","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"token":{"name":"token","type":"option","char":"t","description":"API Token for reporting results"},"benchmark":{"name":"benchmark","type":"option","char":"b","description":"Which benchmark to run"},"delay":{"name":"delay","type":"option","char":"d","description":"delay between runs (s)","default":30}},"args":[{"name":"baseurl","description":"Base URL","required":false}]},"run-js":{"id":"run-js","description":"Run javascript code","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"tenant":{"name":"tenant","type":"option","char":"t","description":"tenant name"},"code":{"name":"code","type":"option","char":"c","description":"js code"},"file":{"name":"file","type":"option","char":"f","description":"path to script file"}},"args":[]},"run-sql":{"id":"run-sql","description":"Run sql expression","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"tenant":{"name":"tenant","type":"option","char":"t","description":"tenant name"},"sql":{"name":"sql","type":"option","char":"s","description":"sql statement"},"file":{"name":"file","type":"option","char":"f","description":"path to sql file name"}},"args":[]},"run-tests":{"id":"run-tests","description":"Run test suites","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"coverage":{"name":"coverage","type":"boolean","char":"c","description":"Coverage","allowNo":false},"listTests":{"name":"listTests","type":"boolean","char":"l","description":"List tests","allowNo":false},"verbose":{"name":"verbose","type":"boolean","char":"v","description":"Verbose","allowNo":false},"detectOpenHandles":{"name":"detectOpenHandles","type":"boolean","char":"d","description":"Detect Open Handles","allowNo":false},"testFilter":{"name":"testFilter","type":"option","char":"t","description":"Filter tests by suite or test name"},"watch":{"name":"watch","type":"boolean","description":"Watch files for changes and rerun tests related to changed files.","allowNo":false},"watchAll":{"name":"watchAll","type":"boolean","description":"Watch files for changes and rerun all tests.","allowNo":false},"database":{"name":"database","type":"option","description":"Run on specified database. Default is saltcorn_test"}},"args":[{"name":"package","description":"which package to run tests for"}]},"run-trigger":{"id":"run-trigger","description":"Run a trigger","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"tenant":{"name":"tenant","type":"option","char":"t","description":"tenant","required":false}},"args":[{"name":"trigger","description":"trigger name","required":true}]},"scheduler":{"id":"scheduler","description":"Run the Saltcorn scheduler","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"verbose":{"name":"verbose","type":"boolean","char":"v","description":"Verbose","allowNo":false}},"args":[]},"serve":{"id":"serve","description":"Start the Saltcorn server","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"port":{"name":"port","type":"option","char":"p","description":"port","default":3000},"verbose":{"name":"verbose","type":"boolean","char":"v","description":"Verbose","allowNo":false},"watchReaper":{"name":"watchReaper","type":"boolean","char":"r","description":"Watch reaper","allowNo":false},"dev":{"name":"dev","type":"boolean","char":"d","description":"Run in dev mode and re-start on file changes","allowNo":false},"addschema":{"name":"addschema","type":"boolean","char":"a","description":"Add schema if missing","allowNo":false},"nomigrate":{"name":"nomigrate","type":"boolean","char":"n","description":"No migrations","allowNo":false},"noscheduler":{"name":"noscheduler","type":"boolean","char":"s","description":"No scheduler","allowNo":false},"subdomain_offset":{"name":"subdomain_offset","type":"option","description":"Number of parts to remove to access subdomain in 'multi_tenant' mode"}},"args":[]},"set-cfg":{"id":"set-cfg","description":"Set a configuration value. The supplied value (argument, or file stdin) will be parsed as JSON. If this fails, it is stored as a string.","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"tenant":{"name":"tenant","type":"option","char":"t","description":"tenant"},"plugin":{"name":"plugin","type":"option","char":"p","description":"plugin"},"file":{"name":"file","type":"option","char":"f","description":"file"},"stdin":{"name":"stdin","type":"boolean","char":"i","description":"read value from stdin","allowNo":false}},"args":[{"name":"key","description":"Configuration key","required":false},{"name":"value","description":"Configuration value (JSON or string)"}]},"setup-benchmark":{"id":"setup-benchmark","description":"Setup an instance for benchmarking","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"tenant":{"name":"tenant","type":"option","char":"t","description":"tenant"}},"args":[]},"setup":{"id":"setup","description":"Set up a new system\n...\nThis will attempt to install or connect a database, and set up a\nconfiguration file\n","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"coverage":{"name":"coverage","type":"boolean","char":"c","description":"Coverage","allowNo":false}},"args":[]},"sync-upload-data":{"id":"sync-upload-data","description":"Runs a sync for data supplied by the mobile app","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"tenantAppName":{"name":"tenantAppName","type":"option","description":"Optional name of a tenant application"},"userEmail":{"name":"userEmail","type":"option","description":"email of the user running the sync"},"directory":{"name":"directory","type":"option","description":"directory name for input output data"},"syncTimestamp":{"name":"syncTimestamp","type":"option","description":"new timestamp for the sync_info rows"}},"args":[]},"take-snapshot":{"id":"take-snapshot","description":"Print a current snapshout to stdout","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"tenant":{"name":"tenant","type":"option","char":"t","description":"tenant"},"fresh":{"name":"fresh","type":"boolean","char":"f","description":"fresh","allowNo":false}},"args":[]},"transform-field":{"id":"transform-field","description":"transform an existing field by applying a calculated expression","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"expression","description":"expression to calculate field","required":true},{"name":"field","description":"field name","required":true},{"name":"table","description":"table name","required":true},{"name":"tenant","description":"tenant name","required":false}]},"dev:localize-plugin":{"id":"dev:localize-plugin","description":"Convert plugin to local plugin","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"unlocalize":{"name":"unlocalize","type":"boolean","char":"u","description":"Unlocalize plugin (local to npm)","allowNo":false},"tenant":{"name":"tenant","type":"option","char":"t","description":"tenant"}},"args":[{"name":"plugin","description":"Current plugin name","required":true},{"name":"path","description":"Absolute path to local plugin"}]},"dev:make-migration":{"id":"dev:make-migration","description":"Create a new blank Database structure migration file.\nThese migrations update database structure.\nYou should not normally need to run this\nunless you are a developer.\n","usage":"make-migration","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{},"args":[]},"dev:plugin-test":{"id":"dev:plugin-test","description":"Install a plugin, spawn 'npm run test' in the install directory and check the return code.","usage":"saltcorn dev:plugin-test -d [PATH_TO_LOCAL_PLUGIN]/statistics -f test-backup.zip","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{"directory":{"name":"directory","type":"option","char":"d","description":"Directory of local plugin"},"name":{"name":"name","type":"option","char":"n","description":"Plugin name in store of a released plugin"},"backupFile":{"name":"backupFile","type":"option","char":"f","description":"Optional name of a backup file in the tests folder. If you ommit this, then the test has to create its own data."},"database":{"name":"database","type":"option","description":"Run on specified database. Default is 'saltcorn_test''"}},"args":[]},"dev:post-release":{"id":"dev:post-release","description":"Post-release tasks: docker and vagrant builds","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"task","description":"What to do","options":["docker","vagrant","all","none"]}]},"dev:release":{"id":"dev:release","description":"Release a new saltcorn version","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"version","description":"New version number","required":true}]},"dev:test-plugin":{"id":"dev:test-plugin","description":"Test a plugin\n...\nExtra documentation goes here\n","pluginName":"@saltcorn/cli","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"path","description":"path to plugin package","required":true}]}}}
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@saltcorn/cli",
3
3
  "description": "Command-line interface for Saltcorn, open-source no-code platform",
4
4
  "homepage": "https://saltcorn.com",
5
- "version": "0.9.0-beta.6",
5
+ "version": "0.9.0-beta.8",
6
6
  "author": "Tom Nielsen @glutamate",
7
7
  "bin": {
8
8
  "saltcorn": "./bin/saltcorn"
@@ -13,11 +13,11 @@
13
13
  "@oclif/config": "^1.18.3",
14
14
  "@oclif/plugin-plugins": "^2.1.0",
15
15
  "@oclif/plugin-help": "^3.3.1",
16
- "@saltcorn/admin-models": "0.9.0-beta.6",
17
- "@saltcorn/data": "0.9.0-beta.6",
18
- "@saltcorn/mobile-app": "0.9.0-beta.6",
19
- "@saltcorn/mobile-builder": "0.9.0-beta.6",
20
- "@saltcorn/server": "0.9.0-beta.6",
16
+ "@saltcorn/admin-models": "0.9.0-beta.8",
17
+ "@saltcorn/data": "0.9.0-beta.8",
18
+ "@saltcorn/mobile-app": "0.9.0-beta.8",
19
+ "@saltcorn/mobile-builder": "0.9.0-beta.8",
20
+ "@saltcorn/server": "0.9.0-beta.8",
21
21
  "cli-ux": "^5.6.7",
22
22
  "contractis": "^0.1.0",
23
23
  "dateformat": "^3.0.3",
@@ -60,37 +60,66 @@ const translateInsertFks = async (allChanges, allTranslations) => {
60
60
  }
61
61
  };
62
62
 
63
+ const checkConstraints = async (table, row) => {
64
+ const uniques = table.constraints.filter((c) => c.type === "Unique");
65
+ for (const { configuration } of uniques) {
66
+ const where = {};
67
+ for (const field of configuration.fields) {
68
+ where[field] = row[field];
69
+ }
70
+ const conflictRow = await table.getRow(where);
71
+ if (conflictRow) return conflictRow;
72
+ }
73
+ return null;
74
+ };
75
+
63
76
  const applyInserts = async (changes, syncTimestamp, user) => {
64
77
  const schema = db.getTenantSchemaPrefix();
65
78
  const allTranslations = {};
79
+ const allUniqueConflicts = {};
66
80
  for (const [tblName, vals] of Object.entries(changes)) {
67
81
  const table = Table.findOne({ name: tblName });
68
82
  if (!table) throw new Error(`The table '${tblName}' does not exists`);
69
- if (vals.inserts?.length > 0) {
70
- const pkName = table.pk_name;
71
- await db.query(
72
- `alter table ${schema}"${db.sqlsanitize(tblName)}" disable trigger all`
73
- );
74
- const translations = {};
75
- for (const insert of vals.inserts || []) {
76
- const row = pickFields(table, pkName, insert);
77
- const newId = await table.insertRow(
78
- row,
79
- user,
80
- undefined,
81
- true,
82
- syncTimestamp
83
+ try {
84
+ if (vals.inserts?.length > 0) {
85
+ const pkName = table.pk_name;
86
+ await db.query(
87
+ `alter table ${schema}"${db.sqlsanitize(
88
+ tblName
89
+ )}" disable trigger all`
90
+ );
91
+ const translations = {};
92
+ const uniqueConflicts = [];
93
+ for (const insert of vals.inserts || []) {
94
+ const row = pickFields(table, pkName, insert);
95
+ const conflictRow = await checkConstraints(table, row);
96
+ if (!conflictRow) {
97
+ const newId = await table.insertRow(
98
+ row,
99
+ user,
100
+ undefined,
101
+ true,
102
+ syncTimestamp
103
+ );
104
+ if (!newId) throw new Error(`Unable to insert into ${tblName}`);
105
+ else if (newId !== insert[pkName])
106
+ translations[insert[pkName]] = newId;
107
+ } else {
108
+ translations[insert[pkName]] = conflictRow[pkName];
109
+ uniqueConflicts.push(conflictRow);
110
+ }
111
+ }
112
+ allTranslations[tblName] = translations;
113
+ allUniqueConflicts[tblName] = uniqueConflicts;
114
+ await db.query(
115
+ `alter table ${schema}"${db.sqlsanitize(tblName)}" enable trigger all`
83
116
  );
84
- if (!newId) throw new Error(`Unable to insert into ${tblName}`);
85
- else if (newId !== insert[pkName]) translations[insert[pkName]] = newId;
86
117
  }
87
- allTranslations[tblName] = translations;
88
- await db.query(
89
- `alter table ${schema}"${db.sqlsanitize(tblName)}" enable trigger all`
90
- );
118
+ } catch (error) {
119
+ throw new Error(table.normalise_error_message(error.message));
91
120
  }
92
121
  }
93
- return allTranslations;
122
+ return { allTranslations, allUniqueConflicts };
94
123
  };
95
124
 
96
125
  const applyUpdates = async (changes, allTranslations, syncTimestamp, user) => {
@@ -98,29 +127,33 @@ const applyUpdates = async (changes, allTranslations, syncTimestamp, user) => {
98
127
  if (vals.updates?.length > 0) {
99
128
  const table = Table.findOne({ name: tblName });
100
129
  if (!table) throw new Error(`The table '${tblName}' does not exists`);
101
- const pkName = table.pk_name;
102
- const insertTranslations = allTranslations[tblName];
103
- for (const update of vals.updates) {
104
- const row = pickFields(table, pkName, update, true);
105
- if (insertTranslations?.[row[pkName]])
106
- row[pkName] = insertTranslations[row[pkName]];
107
- for (const fk of table.getForeignKeys()) {
108
- const oldVal = row[fk.name];
109
- if (oldVal) {
110
- const newVal = allTranslations[fk.reftable_name]?.[oldVal];
111
- if (newVal) row[fk.name] = newVal;
130
+ try {
131
+ const pkName = table.pk_name;
132
+ const insertTranslations = allTranslations[tblName];
133
+ for (const update of vals.updates) {
134
+ const row = pickFields(table, pkName, update, true);
135
+ if (insertTranslations?.[row[pkName]])
136
+ row[pkName] = insertTranslations[row[pkName]];
137
+ for (const fk of table.getForeignKeys()) {
138
+ const oldVal = row[fk.name];
139
+ if (oldVal) {
140
+ const newVal = allTranslations[fk.reftable_name]?.[oldVal];
141
+ if (newVal) row[fk.name] = newVal;
142
+ }
112
143
  }
144
+ const result = await table.updateRow(
145
+ row,
146
+ row[pkName],
147
+ user,
148
+ true,
149
+ undefined,
150
+ undefined,
151
+ syncTimestamp
152
+ );
153
+ if (result) throw new Error(`Unable to update ${tblName}: ${result}`);
113
154
  }
114
- const result = await table.updateRow(
115
- row,
116
- row[pkName],
117
- user,
118
- true,
119
- undefined,
120
- undefined,
121
- syncTimestamp
122
- );
123
- if (result) throw new Error(`Unable to update ${tblName}: ${result}`);
155
+ } catch (error) {
156
+ throw new Error(table.normalise_error_message(error.message));
124
157
  }
125
158
  }
126
159
  }
@@ -163,6 +196,12 @@ const writeTranslatedIds = async (translatedIds, directory) => {
163
196
  await fs.rename(writeName, path.join(directory, "translated-ids.json"));
164
197
  };
165
198
 
199
+ const writeUniqueConflicts = async (uniqueConflicts, directory) => {
200
+ const writeName = path.join(directory, "unique-conflicts.out");
201
+ await fs.writeFile(writeName, JSON.stringify(uniqueConflicts));
202
+ await fs.rename(writeName, path.join(directory, "unique-conflicts.json"));
203
+ };
204
+
166
205
  const writeErrorFile = async (message, directory) => {
167
206
  const writeName = path.join(directory, "error.out");
168
207
  await fs.writeFile(writeName, JSON.stringify({ message }));
@@ -192,12 +231,17 @@ class SyncUploadData extends Command {
192
231
  await loadAllPlugins();
193
232
  await db.begin();
194
233
  inTransaction = true;
195
- const translatedIds = await applyInserts(changes, syncTimestamp, user);
196
- await translateInsertFks(changes, translatedIds);
197
- await applyUpdates(changes, translatedIds, syncTimestamp, user);
234
+ const { allTranslations, allUniqueConflicts } = await applyInserts(
235
+ changes,
236
+ syncTimestamp,
237
+ user
238
+ );
239
+ await translateInsertFks(changes, allTranslations);
240
+ await applyUpdates(changes, allTranslations, syncTimestamp, user);
198
241
  await applyDeletes(changes, user);
199
242
  await db.commit();
200
- await writeTranslatedIds(translatedIds, flags.directory);
243
+ await writeTranslatedIds(allTranslations, flags.directory);
244
+ await writeUniqueConflicts(allUniqueConflicts, flags.directory);
201
245
  } catch (error) {
202
246
  returnCode = 1;
203
247
  getState().log(2, `Unable to sync: ${error.message}`);