From 98eb44b356f23c7f464af9bcbc3c6fa8abb6e078 Mon Sep 17 00:00:00 2001 From: Thierry Carels Date: Sun, 28 Nov 2021 16:37:42 +0100 Subject: [PATCH] Feature/input validation (#1) * fix validation schema * cleanup * fix task.json validation * add input validation * update readme --- .github/workflows/build.yml | 6 +- CHANGELOG.md | 7 ++- README.md | 3 +- app/components/InputsPanel.tsx | 4 +- app/components/inputs/InputBoolean.tsx | 2 +- app/components/inputs/InputInt.tsx | 15 ++++- app/components/inputs/InputMultiLine.tsx | 2 +- app/components/inputs/InputPickList.tsx | 14 ++++- app/components/inputs/InputString.tsx | 14 ++++- app/components/inputs/TaskInput.tsx | 4 +- app/components/models/AdoTask.ts | 2 - app/components/ui/CollapsiblePanel.tsx | 2 +- app/helper/inputExpressionValidation.ts | 73 ++++++++++++++++++++++++ package-lock.json | 34 +++++------ package.json | 4 +- src/models/AzureDevOpsTask.ts | 43 ++++++++------ src/views/AzDoTaskPanel.ts | 5 +- src/views/messages/messageTypes.ts | 7 +-- 18 files changed, 171 insertions(+), 70 deletions(-) create mode 100644 app/helper/inputExpressionValidation.ts diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 34eace4..afe72e4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,7 +2,11 @@ name: Lint, Build & Test on: push: - branches: [ main ] + branches: + - main + pull_request: + branches: + - main jobs: lintBuildTest: diff --git a/CHANGELOG.md b/CHANGELOG.md index e4eb88e..e0d5a1b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,13 @@ # Change Log All notable changes to the "ado-task-viewer" extension will be documented in this file. -## Version 0.1.1 +## Version 0.2.0 +- Add input validation (except "length") + https://github.com/microsoft/azure-pipelines-tasks/blob/master/docs/taskinputvalidation.md +- Fix task.json validation +## Version 0.1.1 - Add clearer readme - Update packages ## Version 0.1.0 - - Initial release \ No newline at end of file diff --git a/README.md b/README.md index c2b06b7..f9bb233 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,13 @@ # AzDevops task.json viewer ## Features -### View task.json of an Azure DevOps custom extension +### View task.json of an Azure DevOps extension
Supported : - input types - visibility rules +- input validation - collapsible groups - help markdown - validation : based on https://github.com/microsoft/azure-pipelines-task-lib/blob/master/tasks.schema.json diff --git a/app/components/InputsPanel.tsx b/app/components/InputsPanel.tsx index 9f4b08e..36162e1 100644 --- a/app/components/InputsPanel.tsx +++ b/app/components/InputsPanel.tsx @@ -21,11 +21,9 @@ export const InputsPanel: React.FC = (props): JSX.Element => { const handleChangeEvent = (inputName: string, value?: string | boolean | undefined) => { const newAdoTask = {...adoTask}; - console.log("updateInputValue : " + inputName + "=" + value + " ("+typeof value+")"); const adoInput = newAdoTask.adoInputs.get(inputName); if (adoInput) { - adoInput.value = value; - console.log("updateInputValue done : " + inputName + "=" + value + " ("+typeof value+")"); + adoInput.value = value; updateVisibilities(newAdoTask); } setAdoTask(newAdoTask); diff --git a/app/components/inputs/InputBoolean.tsx b/app/components/inputs/InputBoolean.tsx index 105df7d..70ad6c0 100644 --- a/app/components/inputs/InputBoolean.tsx +++ b/app/components/inputs/InputBoolean.tsx @@ -1,4 +1,4 @@ -import { Checkbox, ICheckboxProps, ICheckboxStyles, IRenderFunction, ITextFieldStyles } from "@fluentui/react"; +import { Checkbox, ICheckboxStyles, ITextFieldStyles } from "@fluentui/react"; import { useBoolean } from "@fluentui/react-hooks"; import React from "react"; import { LabelInfo } from "../ui/LabelInfo"; diff --git a/app/components/inputs/InputInt.tsx b/app/components/inputs/InputInt.tsx index 430245f..2ac3d8a 100644 --- a/app/components/inputs/InputInt.tsx +++ b/app/components/inputs/InputInt.tsx @@ -1,15 +1,23 @@ -import { IRenderFunction, ITextFieldProps, TextField } from "@fluentui/react"; -import React, { useCallback } from "react"; +import { TextField } from "@fluentui/react"; +import React, { useCallback, useState } from "react"; +import { isExpressionValid } from "../../helper/inputExpressionValidation"; import { LabelInfo } from "../ui/LabelInfo"; import { evaluateFieldAsBoolean, evaluateFieldAsInt, TaskInputProps } from "./TaskInput"; export const InputInt: React.FC = (props): JSX.Element => { + const [errorMessage, setErrorMessage] = useState(undefined); + const expression = props.adoInput.validation?.expression; + const expressionMessage = props.adoInput.validation?.message; + const _handleTextFieldChangeEvent = useCallback( (event: React.FormEvent, newValue?: string | undefined) => { if (props.onChange) { props.onChange(props.adoInput.name, newValue); } + if (expression) { + setErrorMessage(isExpressionValid(expression, newValue) ? undefined : expressionMessage); + } }, [] ); @@ -26,6 +34,7 @@ export const InputInt: React.FC = (props): JSX.Element => { onRenderLabel={_onRenderLabel} defaultValue={props.adoInput.value?.toString()} onChange={_handleTextFieldChangeEvent} - maxLength={evaluateFieldAsInt(props.adoInput.properties?.maxLength)} />; + maxLength={evaluateFieldAsInt(props.adoInput.properties?.maxLength)} + errorMessage={errorMessage} />; }; \ No newline at end of file diff --git a/app/components/inputs/InputMultiLine.tsx b/app/components/inputs/InputMultiLine.tsx index 9dca9f2..0a6e098 100644 --- a/app/components/inputs/InputMultiLine.tsx +++ b/app/components/inputs/InputMultiLine.tsx @@ -1,4 +1,4 @@ -import { IRenderFunction, ITextFieldProps, TextField } from "@fluentui/react"; +import { TextField } from "@fluentui/react"; import React, { useCallback } from "react"; import { LabelInfo } from "../ui/LabelInfo"; import { evaluateFieldAsBoolean, evaluateFieldAsInt, TaskInputProps } from "./TaskInput"; diff --git a/app/components/inputs/InputPickList.tsx b/app/components/inputs/InputPickList.tsx index 59673a2..df82e22 100644 --- a/app/components/inputs/InputPickList.tsx +++ b/app/components/inputs/InputPickList.tsx @@ -1,6 +1,7 @@ import { ComboBox, Dropdown, IComboBox, IComboBoxOption, IDropdownOption } from "@fluentui/react"; -import React from "react"; +import React, { useState } from "react"; import { DataSourceBinding, Options } from "../../../src/models/AzureDevOpsTask"; +import { isExpressionValid } from "../../helper/inputExpressionValidation"; import { LabelInfo } from "../ui/LabelInfo"; import { evaluateFieldAsStringArray, evaluateFieldAsBoolean, TaskInputProps } from "./TaskInput"; @@ -49,7 +50,7 @@ export const InputPickList: React.FC = (props): JSX.Element => { } return options; }; - + const [options, setOptions] = React.useState(_initOptions(props.type, props.adoInput.value, props.adoInput.options, props.adoInput.dataSourceBinding)); const [selectedKey, setSelectedKey] = React.useState(props.adoInput.value?.toString()); @@ -61,6 +62,10 @@ export const InputPickList: React.FC = (props): JSX.Element => { }, []); + const [errorMessage, setErrorMessage] = useState(undefined); + const expression = props.adoInput.validation?.expression; + const expressionMessage = props.adoInput.validation?.message; + const _handleComboboxChangeEvent = React.useCallback( (event: React.FormEvent, option?: IComboBoxOption | undefined, index?: number | undefined, value?: string | undefined) => { let selected = option?.selected; @@ -69,10 +74,12 @@ export const InputPickList: React.FC = (props): JSX.Element => { option = { key: value, text: value }; setOptions(prevOptions => [...prevOptions, option!]); } - if (option) { setSelectedKey(option.key); } + if (expression) { + setErrorMessage(isExpressionValid(expression, value ?? option?.key.toString()) ? undefined : expressionMessage); + } if (props.onChange) { props.onChange(props.adoInput.name, value ?? option?.key.toString() ?? ""); } @@ -97,6 +104,7 @@ export const InputPickList: React.FC = (props): JSX.Element => { options={options} selectedKey={selectedKey} onChange={_handleComboboxChangeEvent} + errorMessage={errorMessage} useComboBoxAsMenuWidth /> ; } else { diff --git a/app/components/inputs/InputString.tsx b/app/components/inputs/InputString.tsx index 43f3cc2..cb59908 100644 --- a/app/components/inputs/InputString.tsx +++ b/app/components/inputs/InputString.tsx @@ -1,15 +1,22 @@ -import { IRenderFunction, ITextFieldProps, TextField } from "@fluentui/react"; -import React, { useCallback } from "react"; +import { TextField } from "@fluentui/react"; +import React, { useCallback, useState } from "react"; import { LabelInfo } from "../ui/LabelInfo"; import { evaluateFieldAsBoolean, evaluateFieldAsInt, TaskInputProps } from "./TaskInput"; +import { isExpressionValid } from "../../helper/inputExpressionValidation"; export const InputString: React.FC = (props): JSX.Element => { + const [errorMessage, setErrorMessage] = useState(undefined); + const expression = props.adoInput.validation?.expression; + const expressionMessage = props.adoInput.validation?.message; const _handleTextFieldChangeEvent = useCallback( (event: React.FormEvent, newValue?: string | undefined) => { if (props.onChange) { props.onChange(props.adoInput.name, newValue); } + if (expression) { + setErrorMessage(isExpressionValid(expression, newValue) ? undefined : expressionMessage); + } }, [] ); @@ -25,5 +32,6 @@ export const InputString: React.FC = (props): JSX.Element => { onRenderLabel={_onRenderLabel} defaultValue={props.adoInput.value?.toString()} onChange={_handleTextFieldChangeEvent} - maxLength={evaluateFieldAsInt(props.adoInput.properties?.maxLength)} />; + maxLength={evaluateFieldAsInt(props.adoInput.properties?.maxLength)} + errorMessage={errorMessage} />; }; \ No newline at end of file diff --git a/app/components/inputs/TaskInput.tsx b/app/components/inputs/TaskInput.tsx index 86c3b63..f6b25f3 100644 --- a/app/components/inputs/TaskInput.tsx +++ b/app/components/inputs/TaskInput.tsx @@ -19,9 +19,9 @@ export const evaluateFieldAsBoolean = (value: string | boolean | number | undefi return value.toString().toLowerCase() === "true"; }; -export const evaluateFieldAsInt = (value: string | undefined, defaultValue: number | undefined = undefined): number | undefined => { +export const evaluateFieldAsInt = (value: string | number | undefined, defaultValue: number | undefined = undefined): number | undefined => { if (value === undefined) { return defaultValue; } - return parseInt(value); + return parseInt(value.toString()); }; \ No newline at end of file diff --git a/app/components/models/AdoTask.ts b/app/components/models/AdoTask.ts index 6b791b9..56b2231 100644 --- a/app/components/models/AdoTask.ts +++ b/app/components/models/AdoTask.ts @@ -36,13 +36,11 @@ export function updateVisibilities(adoTask: AdoTask) { [...adoTask.adoGroups.values()].map((adoGroup) => { if (adoGroup.visibilityRule) { adoGroup.isVisible = VisibilityHelper.evaluateVisibility(adoGroup.visibilityRule, adoTask.adoInputs); - //console.log("evaluate group '" + adoGroup.name + "': [" + adoGroup.visibleRule + "] --> " + adoGroup.isVisible); } if (adoGroup.isVisible) { [...adoGroup.adoInputs.values()].map((adoInput) => { if (adoInput.visibilityRule) { adoInput.isVisible = VisibilityHelper.evaluateVisibility(adoInput.visibilityRule, adoTask.adoInputs); - //console.log("evaluate input '" + adoInput.name + "': [" + adoInput.visibleRule + "] --> " + adoInput.isVisible); } }); } diff --git a/app/components/ui/CollapsiblePanel.tsx b/app/components/ui/CollapsiblePanel.tsx index e81ca64..bc1c047 100644 --- a/app/components/ui/CollapsiblePanel.tsx +++ b/app/components/ui/CollapsiblePanel.tsx @@ -1,4 +1,4 @@ -import { getColorFromString, Icon, IStackStyles, ITextFieldStyles, Label, Stack } from "@fluentui/react"; +import { Icon, IStackStyles, ITextFieldStyles, Label, Stack } from "@fluentui/react"; import { useBoolean } from "@fluentui/react-hooks"; import React from "react"; import { Collapse } from "react-collapse"; diff --git a/app/helper/inputExpressionValidation.ts b/app/helper/inputExpressionValidation.ts new file mode 100644 index 0000000..5861868 --- /dev/null +++ b/app/helper/inputExpressionValidation.ts @@ -0,0 +1,73 @@ +function isUrl(value: string): boolean { + const regexp = /(https?|ftp):\/\/[^\s/$.?#].[^\s]*$/; + return regexp.test(value); +} + +function isIpV4Address(value: string): boolean { + const regexp = /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/; + return regexp.test(value); +} + +function isEmail(value: string): boolean { + const regexp = /^([\w-.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/; + return regexp.test(value); +} + +function isInRange(expression: string, value: string): boolean { + const expr = expression.substring(9).slice(1, -1); + const splitted = expr.split(","); + const min = Number(splitted[1]); + const max = Number(splitted[2]); + const val = Number(value); + return (min <= val) && (val <= max); +} + +function isSha1(value: string): boolean { + const regexp = /[a-fA-F0-9]{40}/; + return regexp.test(value); +} + +function isMatch(expression: string, value: string): boolean { + const expr = expression.substring(7).slice(1, -1); + const splitted = expr.match(/('.*?'|[^',\s]+)(?=\s*,|\s*$)/g); + if (splitted === null || splitted.length < 3) { + return false; + } + const rawRegex = splitted[1].slice(1, -1); + const rawOptions = splitted[2].slice(1, -1).toLowerCase(); + const options = (rawOptions.includes("ignorecase") ? "i" : "") + (rawOptions.includes("multiline") ? "m" : ""); + + const regex = new RegExp(rawRegex, options); + return regex.test(value); +} + +// https://github.com/Microsoft/azure-pipelines-tasks/blob/master/docs/taskinputvalidation.md +export function isExpressionValid(expression: string | undefined, value: string | undefined): boolean { + if (!expression || expression.length === 0) { + return true; + } + let result = false; + try { + if (expression.startsWith("isMatch(")) { + result = (value) ? isMatch(expression, value) : false; + } else if (expression.startsWith("isInRange(")) { + result = (value) ? isInRange(expression, value) : false; + } else if (expression.startsWith("isUrl(")) { + result = (value) ? isUrl(value) : false; + } else if (expression.startsWith("isIpV4Address(")) { + result = (value) ? isIpV4Address(value) : false; + } else if (expression.startsWith("isEmail(")) { + result = (value) ? isEmail(value) : false; + } else if (expression.startsWith("isSha1(")) { + result = (value) ? isSha1(value) : false; + } else if (expression.startsWith("length(")) { + result = true; // TODO + } else { + result = false; + } + } catch (ex) { + console.error(ex); + return false; + } + return result; +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index ed9c7f8..9780549 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ado-task-viewer", - "version": "0.1.0", + "version": "0.2.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "ado-task-viewer", - "version": "0.1.0", + "version": "0.2.0", "dependencies": { "@fluentui/react": "^8.44.0", "@fluentui/react-hooks": "^8.3.7", @@ -31,7 +31,7 @@ "style-loader": "^3.3.1", "ts-loader": "^9.2.6", "typescript": "^4.5.2", - "webpack": "^5.64.3", + "webpack": "^5.64.4", "webpack-cli": "^4.9.1" }, "engines": { @@ -3760,9 +3760,9 @@ } }, "node_modules/watchpack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.2.0.tgz", - "integrity": "sha512-up4YAn/XHgZHIxFBVCdlMiWDj6WaLKpwVeGQk2I5thdYxF/KmF0aaz6TfJZ/hfl1h/XlcDr7k1KH7ThDagpFaA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.0.tgz", + "integrity": "sha512-MnN0Q1OsvB/GGHETrFeZPQaOelWh/7O+EiFlj8sM9GPjtQkis7k01aAxrg/18kTfoIVcLL+haEVFlXDaSRwKRw==", "dev": true, "dependencies": { "glob-to-regexp": "^0.4.1", @@ -3773,9 +3773,9 @@ } }, "node_modules/webpack": { - "version": "5.64.3", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.64.3.tgz", - "integrity": "sha512-XF6/IL9Bw2PPQioiR1UYA8Bs4tX3QXJtSelezKECdLFeSFzWoe44zqTzPW5N+xI3fACaRl2/G3sNA4WYHD7Iww==", + "version": "5.64.4", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.64.4.tgz", + "integrity": "sha512-LWhqfKjCLoYJLKJY8wk2C3h77i8VyHowG3qYNZiIqD6D0ZS40439S/KVuc/PY48jp2yQmy0mhMknq8cys4jFMw==", "dev": true, "dependencies": { "@types/eslint-scope": "^3.7.0", @@ -3800,7 +3800,7 @@ "schema-utils": "^3.1.0", "tapable": "^2.1.1", "terser-webpack-plugin": "^5.1.3", - "watchpack": "^2.2.0", + "watchpack": "^2.3.0", "webpack-sources": "^3.2.2" }, "bin": { @@ -6743,9 +6743,9 @@ } }, "watchpack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.2.0.tgz", - "integrity": "sha512-up4YAn/XHgZHIxFBVCdlMiWDj6WaLKpwVeGQk2I5thdYxF/KmF0aaz6TfJZ/hfl1h/XlcDr7k1KH7ThDagpFaA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.0.tgz", + "integrity": "sha512-MnN0Q1OsvB/GGHETrFeZPQaOelWh/7O+EiFlj8sM9GPjtQkis7k01aAxrg/18kTfoIVcLL+haEVFlXDaSRwKRw==", "dev": true, "requires": { "glob-to-regexp": "^0.4.1", @@ -6753,9 +6753,9 @@ } }, "webpack": { - "version": "5.64.3", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.64.3.tgz", - "integrity": "sha512-XF6/IL9Bw2PPQioiR1UYA8Bs4tX3QXJtSelezKECdLFeSFzWoe44zqTzPW5N+xI3fACaRl2/G3sNA4WYHD7Iww==", + "version": "5.64.4", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.64.4.tgz", + "integrity": "sha512-LWhqfKjCLoYJLKJY8wk2C3h77i8VyHowG3qYNZiIqD6D0ZS40439S/KVuc/PY48jp2yQmy0mhMknq8cys4jFMw==", "dev": true, "requires": { "@types/eslint-scope": "^3.7.0", @@ -6780,7 +6780,7 @@ "schema-utils": "^3.1.0", "tapable": "^2.1.1", "terser-webpack-plugin": "^5.1.3", - "watchpack": "^2.2.0", + "watchpack": "^2.3.0", "webpack-sources": "^3.2.2" }, "dependencies": { diff --git a/package.json b/package.json index b1d1ddc..9ec2be5 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "AzDo Task Viewer", "icon": "ado-task-viewer.png", "publisher": "carth", - "version": "0.1.1", + "version": "0.2.0", "private": "true", "repository": { "type": "git", @@ -75,7 +75,7 @@ "style-loader": "^3.3.1", "ts-loader": "^9.2.6", "typescript": "^4.5.2", - "webpack": "^5.64.3", + "webpack": "^5.64.4", "webpack-cli": "^4.9.1" }, "dependencies": { diff --git a/src/models/AzureDevOpsTask.ts b/src/models/AzureDevOpsTask.ts index aa6030e..0c5c98e 100644 --- a/src/models/AzureDevOpsTask.ts +++ b/src/models/AzureDevOpsTask.ts @@ -246,18 +246,18 @@ export interface Input { export type Resizable = boolean | string; export interface Properties { - DisableManageLink?: DisableManageLink; - EditableOptions?: EditableOptions; + DisableManageLink?: boolean | DisableManageLink; + EditableOptions?: boolean | EditableOptions; editorExtension?: string; EndpointFilterRule?: string; - IsSearchable?: IsSearchable; - isVariableOrNonNegativeNumber?: IsVariableOrNonNegativeNumber; + IsSearchable?: boolean | IsSearchable; + isVariableOrNonNegativeNumber?: boolean | IsVariableOrNonNegativeNumber; maxLength?: string; - MultiSelect?: MultiSelect; - MultiSelectFlatList?: MultiSelectFlatList; - PopulateDefaultValue?: PopulateDefaultValue; - resizable?: Resizable; - rows?: string; + MultiSelect?: boolean | MultiSelect; + MultiSelectFlatList?: boolean | MultiSelectFlatList; + PopulateDefaultValue?: boolean | PopulateDefaultValue; + resizable?: boolean | Resizable; + rows?: string | number; } export enum DisableManageLink { @@ -651,18 +651,19 @@ const typeMap: any = { { json: "validation", js: "validation", typ: u(undefined, r("Validation")) }, ], false), "Properties": o([ - { json: "DisableManageLink", js: "disableManageLink", typ: u(undefined, r("DisableManageLink")) }, - { json: "EditableOptions", js: "editableOptions", typ: u(undefined, r("EditableOptions")) }, + { json: "DisableManageLink", js: "disableManageLink", typ: u(undefined, u(true, r("DisableManageLink"))) }, + { json: "EditableOptions", js: "editableOptions", typ: u(undefined, u(true, r("EditableOptions"))) }, { json: "editorExtension", js: "editorExtension", typ: u(undefined, "") }, { json: "EndpointFilterRule", js: "endpointFilterRule", typ: u(undefined, "") }, - { json: "IsSearchable", js: "isSearchable", typ: u(undefined, r("IsSearchable")) }, - { json: "isVariableOrNonNegativeNumber", js: "isVariableOrNonNegativeNumber", typ: u(undefined, r("IsVariableOrNonNegativeNumber")) }, + { json: "IsSearchable", js: "isSearchable", typ: u(undefined, u(true, r("IsSearchable"))) }, + { json: "isVariableOrNonNegativeNumber", js: "isVariableOrNonNegativeNumber", typ: u(undefined, u(true, r("IsVariableOrNonNegativeNumber"))) }, { json: "maxLength", js: "maxLength", typ: u(undefined, "") }, - { json: "MultiSelect", js: "multiSelect", typ: u(undefined, r("MultiSelect")) }, - { json: "MultiSelectFlatList", js: "multiSelectFlatList", typ: u(undefined, r("MultiSelectFlatList")) }, - { json: "PopulateDefaultValue", js: "populateDefaultValue", typ: u(undefined, r("PopulateDefaultValue")) }, - { json: "resizable", js: "resizable", typ: u(undefined, u(true, "")) }, - { json: "rows", js: "rows", typ: u(undefined, "") }, + { json: "MultiSelect", js: "multiSelect", typ: u(undefined, u(true, r("MultiSelect"))) }, + { json: "MultiSelectFlatList", js: "multiSelectFlatList", typ: u(undefined, u(true, r("MultiSelectFlatList"))) }, + { json: "PopulateDefaultValue", js: "populateDefaultValue", typ: u(undefined, u(true, r("PopulateDefaultValue"))) }, + { json: "resizable", js: "resizable", typ: u(undefined, u(true, r("Resizable"))) }, + { json: "rows", js: "rows", typ: u(undefined, u("",1)) }, + { json: "displayFormat", js: "displayFormat", typ: u(undefined, "") }, ], "any"), "Validation": o([ { json: "expression", js: "expression", typ: u(undefined, "")}, @@ -760,6 +761,12 @@ const typeMap: any = { "false", "true", ], + "Resizable": [ + "False", + "True", + "false", + "true", + ], "IsSearchable": [ "False", "True", diff --git a/src/views/AzDoTaskPanel.ts b/src/views/AzDoTaskPanel.ts index 2049030..9496688 100644 --- a/src/views/AzDoTaskPanel.ts +++ b/src/views/AzDoTaskPanel.ts @@ -2,7 +2,7 @@ import * as vscode from "vscode"; import * as path from "path"; import { ViewColumn } from "vscode"; import { AzureDevOpsTask } from "../models/AzureDevOpsTask"; -import { CommonMessage, Message } from "./messages/messageTypes"; +import { Message } from "./messages/messageTypes"; export class AzDoTaskPanel { public static currentPanel: AzDoTaskPanel | undefined; @@ -24,9 +24,6 @@ export class AzDoTaskPanel { if (AzDoTaskPanel.currentPanel) { AzDoTaskPanel.render(AzDoTaskPanel.currentPanel._fileUri, AzDoTaskPanel.currentPanel._extensionPath); } - } else if (message.type === 'COMMON') { - const text = (message as CommonMessage).payload; - vscode.window.showInformationMessage(`Received message from Webview: ${text}`); } }, null, diff --git a/src/views/messages/messageTypes.ts b/src/views/messages/messageTypes.ts index 3af9103..86f34b2 100644 --- a/src/views/messages/messageTypes.ts +++ b/src/views/messages/messageTypes.ts @@ -1,15 +1,10 @@ -export type MessageType = 'RELOAD' | 'COMMON'; +export type MessageType = 'RELOAD'; export interface Message { type: MessageType; payload?: any; } -export interface CommonMessage extends Message { - type: 'COMMON'; - payload: string; -} - export interface ReloadMessage extends Message { type: 'RELOAD'; }