diff --git a/packages/app-cli/tests/services/plugins/defaultPluginsUtils.ts b/packages/app-cli/tests/services/plugins/defaultPluginsUtils.ts index ce3c6e7858..7d35ae789c 100644 --- a/packages/app-cli/tests/services/plugins/defaultPluginsUtils.ts +++ b/packages/app-cli/tests/services/plugins/defaultPluginsUtils.ts @@ -1,7 +1,7 @@ import { installDefaultPlugins, getDefaultPluginsInstallState, setSettingsForDefaultPlugins, checkPreInstalledDefaultPlugins } from '@joplin/lib/services/plugins/defaultPlugins/defaultPluginsUtils'; import PluginRunner from '../../../app/services/plugins/PluginRunner'; import { pathExists } from 'fs-extra'; -import { setupDatabaseAndSynchronizer, supportDir, switchClient } from '@joplin/lib/testing/test-utils'; +import { checkThrow, setupDatabaseAndSynchronizer, supportDir, switchClient } from '@joplin/lib/testing/test-utils'; import PluginService, { defaultPluginSetting, DefaultPluginsInfo, PluginSettings } from '@joplin/lib/services/plugins/PluginService'; import Setting from '@joplin/lib/models/Setting'; @@ -213,4 +213,57 @@ describe('defaultPluginsUtils', function() { await service.destroy(); }); + it('should not throw error on missing setting key', async () => { + + const service = newPluginService(); + + const pluginScript = ` + /* joplin-manifest: + { + "id": "io.github.jackgruber.backup", + "manifest_version": 1, + "app_min_version": "1.4", + "name": "JS Bundle test", + "version": "1.0.0" + } + */ + joplin.plugins.register({ + onStart: async function() { + await joplin.settings.registerSettings({ + path: { + value: "initial-path", + type: 2, + section: "backupSection", + public: true, + label: "Backup path", + }, + }) + }, + });`; + + const plugin = await service.loadPluginFromJsBundle('', pluginScript); + await service.runPlugin(plugin); + + const defaultPluginsInfo: DefaultPluginsInfo = { + 'io.github.jackgruber.backup': { + version: '1.0.2', + settings: { + 'path': `${Setting.value('profileDir')}`, + 'missing-key1': 'someValue', + }, + }, + 'plugin.calebjohn.rich-markdown': { + version: '0.8.3', + settings: { + 'missing-key2': 'someValue', + }, + }, + }; + + Setting.setValue('installedDefaultPlugins', ['']); + expect(checkThrow(() => setSettingsForDefaultPlugins(defaultPluginsInfo))).toBe(false); + expect(Setting.value('plugin-io.github.jackgruber.backup.path')).toBe(`${Setting.value('profileDir')}`); + await service.destroy(); + }); + }); diff --git a/packages/app-desktop/app.ts b/packages/app-desktop/app.ts index d692f61575..b72cb39eee 100644 --- a/packages/app-desktop/app.ts +++ b/packages/app-desktop/app.ts @@ -278,8 +278,8 @@ class Application extends BaseApplication { checkPreInstalledDefaultPlugins(defaultPluginsId, pluginSettings); try { - const pluginsDir = path.join(bridge().buildDir(), 'defaultPlugins'); - pluginSettings = await installDefaultPlugins(service, pluginsDir, defaultPluginsId, pluginSettings); + const defaultPluginsDir = path.join(bridge().buildDir(), 'defaultPlugins'); + pluginSettings = await installDefaultPlugins(service, defaultPluginsDir, defaultPluginsId, pluginSettings); if (await shim.fsDriver().exists(Setting.value('pluginDir'))) { await service.loadAndRunPlugins(Setting.value('pluginDir'), pluginSettings); } diff --git a/packages/lib/services/plugins/defaultPlugins/defaultPluginsUtils.ts b/packages/lib/services/plugins/defaultPlugins/defaultPluginsUtils.ts index 50ebc0db97..70124df9f8 100644 --- a/packages/lib/services/plugins/defaultPlugins/defaultPluginsUtils.ts +++ b/packages/lib/services/plugins/defaultPlugins/defaultPluginsUtils.ts @@ -3,9 +3,12 @@ import path = require('path'); import Setting from '../../../models/Setting'; import shim from '../../../shim'; import PluginService, { defaultPluginSetting, DefaultPluginsInfo, PluginSettings } from '../PluginService'; +import Logger from '@joplin/lib/Logger'; import * as React from 'react'; const shared = require('@joplin/lib/components/shared/config-shared.js'); +const logger = Logger.create('defaultPluginsUtils'); + export function checkPreInstalledDefaultPlugins(defaultPluginsId: string[],pluginSettings: PluginSettings) { const installedDefaultPlugins: Array = Setting.value('installedDefaultPlugins'); for (const pluginId of defaultPluginsId) { @@ -15,8 +18,17 @@ export function checkPreInstalledDefaultPlugins(defaultPluginsId: string[],plugi } } -export async function installDefaultPlugins(service: PluginService, pluginsDir: string, defaultPluginsId: string[], pluginSettings: PluginSettings): Promise { - const defaultPluginsPaths = await shim.fsDriver().readDirStats(pluginsDir); +export async function installDefaultPlugins(service: PluginService, defaultPluginsDir: string, defaultPluginsId: string[], pluginSettings: PluginSettings): Promise { + if (!await shim.fsDriver().exists(defaultPluginsDir)) { + logger.info(`Could not find default plugins' directory: ${defaultPluginsDir} - skipping installation.`); + return pluginSettings; + } + const defaultPluginsPaths = await shim.fsDriver().readDirStats(defaultPluginsDir); + if (defaultPluginsPaths.length <= 0) { + logger.info(`Default plugins' directory is empty: ${defaultPluginsDir} - skipping installation.`); + return pluginSettings; + } + const installedPlugins = Setting.value('installedDefaultPlugins'); for (let pluginId of defaultPluginsPaths) { @@ -24,7 +36,7 @@ export async function installDefaultPlugins(service: PluginService, pluginsDir: // if pluginId is present in 'installedDefaultPlugins' array or it doesn't have default plugin ID, then we won't install it again as default plugin if (installedPlugins.includes(pluginId) || !defaultPluginsId.includes(pluginId)) continue; - const defaultPluginPath: string = path.join(pluginsDir, pluginId, 'plugin.jpl'); + const defaultPluginPath: string = path.join(defaultPluginsDir, pluginId, 'plugin.jpl'); await service.installPlugin(defaultPluginPath, false); pluginSettings = produce(pluginSettings, (draft: PluginSettings) => { @@ -41,7 +53,7 @@ export function setSettingsForDefaultPlugins(defaultPluginsInfo: DefaultPluginsI for (const pluginId of Object.keys(defaultPluginsInfo)) { if (!defaultPluginsInfo[pluginId].settings) continue; for (const settingName of Object.keys(defaultPluginsInfo[pluginId].settings)) { - if (!installedDefaultPlugins.includes(pluginId)) { + if (!installedDefaultPlugins.includes(pluginId) && Setting.keyExists(`plugin-${pluginId}.${settingName}`)) { Setting.setValue(`plugin-${pluginId}.${settingName}`, defaultPluginsInfo[pluginId].settings[settingName]); } }