From 4c854bacb6a8bb7b7926dccc5749a45224421651 Mon Sep 17 00:00:00 2001 From: vince-fugnitto Date: Tue, 31 Aug 2021 13:50:55 -0400 Subject: [PATCH] task: make `required` optional The commit makes `TaskDefinition.properties.required` optional based on the json schema validation definition. Previously, if an extension defines a schema that omits the `required` property the application will throw errors and fail to display the menu. The change treats the missing `required` property as an empty array as per the spec. Signed-off-by: vince-fugnitto --- .../plugin-ext/src/hosted/node/scanners/scanner-theia.ts | 2 +- packages/task/src/browser/provided-task-configurations.ts | 7 ++++--- packages/task/src/browser/task-configurations.ts | 2 +- packages/task/src/browser/task-definition-registry.ts | 7 ++++--- packages/task/src/browser/task-schema-updater.ts | 3 ++- packages/task/src/common/task-protocol.ts | 6 +++++- 6 files changed, 17 insertions(+), 10 deletions(-) diff --git a/packages/plugin-ext/src/hosted/node/scanners/scanner-theia.ts b/packages/plugin-ext/src/hosted/node/scanners/scanner-theia.ts index 276ae053314e1..4a5b047947b63 100644 --- a/packages/plugin-ext/src/hosted/node/scanners/scanner-theia.ts +++ b/packages/plugin-ext/src/hosted/node/scanners/scanner-theia.ts @@ -698,7 +698,7 @@ export class TheiaPluginScanner implements PluginScanner { taskType: definitionContribution.type, source: pluginName, properties: { - required: definitionContribution.required, + required: definitionContribution.required || [], all: propertyKeys, schema: definitionContribution } diff --git a/packages/task/src/browser/provided-task-configurations.ts b/packages/task/src/browser/provided-task-configurations.ts index 5253e63310d1f..3d770e6c0f252 100644 --- a/packages/task/src/browser/provided-task-configurations.ts +++ b/packages/task/src/browser/provided-task-configurations.ts @@ -102,11 +102,12 @@ export class ProvidedTaskConfigurations { const tasks = await this.getTasks(token); for (const task of tasks) { // find detected tasks that match the `definition` let score = 0; - if (!definition.properties.required.every(requiredProp => customization[requiredProp] !== undefined)) { + const required = definition.properties.required || []; + if (!required.every(requiredProp => customization[requiredProp] !== undefined)) { continue; } - score += definition.properties.required.length; // number of required properties - const requiredProps = new Set(definition.properties.required); + score += required.length; // number of required properties + const requiredProps = new Set(required); // number of optional properties score += definition.properties.all.filter(p => !requiredProps.has(p) && customization[p] !== undefined).length; if (score >= highest) { diff --git a/packages/task/src/browser/task-configurations.ts b/packages/task/src/browser/task-configurations.ts index 26fe7af24d574..a97c817938d42 100644 --- a/packages/task/src/browser/task-configurations.ts +++ b/packages/task/src/browser/task-configurations.ts @@ -250,7 +250,7 @@ export class TaskConfigurations implements Disposable { const taskDefinition = this.taskDefinitionRegistry.getDefinition(taskConfig); if (taskDefinition) { const cus = customizationByType.filter(customization => - taskDefinition.properties.required.every(rp => customization[rp] === taskConfig[rp]) + taskDefinition.properties.required?.every(rp => customization[rp] === taskConfig[rp]) )[0]; // Only support having one customization per task return cus; } diff --git a/packages/task/src/browser/task-definition-registry.ts b/packages/task/src/browser/task-definition-registry.ts index ae8154de23d2a..fa4f4a27a009c 100644 --- a/packages/task/src/browser/task-definition-registry.ts +++ b/packages/task/src/browser/task-definition-registry.ts @@ -72,11 +72,12 @@ export class TaskDefinitionRegistry { let highest = -1; for (const def of definitions) { let score = 0; - if (!def.properties.required.every(requiredProp => taskConfiguration[requiredProp] !== undefined)) { + const required = def.properties.required || []; + if (!required.every(requiredProp => taskConfiguration[requiredProp] !== undefined)) { continue; } - score += def.properties.required.length; // number of required properties - const requiredProps = new Set(def.properties.required); + score += required.length; // number of required properties + const requiredProps = new Set(required); // number of optional properties score += def.properties.all.filter(p => !requiredProps.has(p) && taskConfiguration[p] !== undefined).length; if (score > highest) { diff --git a/packages/task/src/browser/task-schema-updater.ts b/packages/task/src/browser/task-schema-updater.ts index 0c698af967730..bd07660b74672 100644 --- a/packages/task/src/browser/task-schema-updater.ts +++ b/packages/task/src/browser/task-schema-updater.ts @@ -168,8 +168,9 @@ export class TaskSchemaUpdater implements JsonSchemaContribution { description: 'The task type to customize' }; customizedDetectedTask.properties!.type = taskType; + const required = def.properties.required || []; def.properties.all.forEach(taskProp => { - if (!!def.properties.required.find(requiredProp => requiredProp === taskProp)) { // property is mandatory + if (required.find(requiredProp => requiredProp === taskProp)) { // property is mandatory customizedDetectedTask.required!.push(taskProp); } customizedDetectedTask.properties![taskProp] = { ...def.properties.schema.properties![taskProp] }; diff --git a/packages/task/src/common/task-protocol.ts b/packages/task/src/common/task-protocol.ts index 548851ec312ec..6ec6b40756b78 100644 --- a/packages/task/src/common/task-protocol.ts +++ b/packages/task/src/common/task-protocol.ts @@ -274,7 +274,11 @@ export interface TaskDefinition { taskType: string; source: string; properties: { - required: string[]; + /** + * Should be treated as an empty array if omitted. + * https://json-schema.org/draft/2020-12/json-schema-validation.html#rfc.section.6.5.3 + */ + required?: string[]; all: string[]; schema: IJSONSchema; }