mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-05-24 09:14:47 +01:00
Fix input validation patterns for resource ids (#2086)
* Fix input element pattern matching and add validation reporting for cases where the element is not within a form element. * Update test snapshots. * Remove old code and fix trigger error message. * Move id validation to a util class. * Add unit tests, fix standalone function, rename constants.
This commit is contained in:
parent
a4c9a47d4e
commit
41f5401016
@ -42,6 +42,7 @@ import {
|
|||||||
isVectorSearchEnabled,
|
isVectorSearchEnabled,
|
||||||
} from "Utils/CapabilityUtils";
|
} from "Utils/CapabilityUtils";
|
||||||
import { getUpsellMessage } from "Utils/PricingUtils";
|
import { getUpsellMessage } from "Utils/PricingUtils";
|
||||||
|
import { ValidCosmosDbIdDescription, ValidCosmosDbIdInputPattern } from "Utils/ValidationUtils";
|
||||||
import { CollapsibleSectionComponent } from "../Controls/CollapsiblePanel/CollapsibleSectionComponent";
|
import { CollapsibleSectionComponent } from "../Controls/CollapsiblePanel/CollapsibleSectionComponent";
|
||||||
import { ThroughputInput } from "../Controls/ThroughputInput/ThroughputInput";
|
import { ThroughputInput } from "../Controls/ThroughputInput/ThroughputInput";
|
||||||
import "../Controls/ThroughputInput/ThroughputInput.less";
|
import "../Controls/ThroughputInput/ThroughputInput.less";
|
||||||
@ -351,8 +352,8 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
required
|
required
|
||||||
type="text"
|
type="text"
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
pattern="[^/?#\\]*[^/?# \\]"
|
pattern={ValidCosmosDbIdInputPattern.source}
|
||||||
title="May not end with space nor contain characters '\' '/' '#' '?'"
|
title={ValidCosmosDbIdDescription}
|
||||||
placeholder="Type a new database id"
|
placeholder="Type a new database id"
|
||||||
size={40}
|
size={40}
|
||||||
className="panelTextField"
|
className="panelTextField"
|
||||||
@ -459,8 +460,8 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
aria-required
|
aria-required
|
||||||
required
|
required
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
pattern="[^/?#\\]*[^/?# \\]"
|
pattern={ValidCosmosDbIdInputPattern.source}
|
||||||
title="May not end with space nor contain characters '\' '/' '#' '?'"
|
title={ValidCosmosDbIdDescription}
|
||||||
placeholder={`e.g., ${getCollectionName()}1`}
|
placeholder={`e.g., ${getCollectionName()}1`}
|
||||||
size={40}
|
size={40}
|
||||||
className="panelTextField"
|
className="panelTextField"
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { Checkbox, Stack, Text, TextField } from "@fluentui/react";
|
import { Checkbox, Stack, Text, TextField } from "@fluentui/react";
|
||||||
import { getNewDatabaseSharedThroughputDefault } from "Common/DatabaseUtility";
|
import { getNewDatabaseSharedThroughputDefault } from "Common/DatabaseUtility";
|
||||||
|
import { ValidCosmosDbIdDescription, ValidCosmosDbIdInputPattern } from "Utils/ValidationUtils";
|
||||||
import React, { FunctionComponent, useEffect, useState } from "react";
|
import React, { FunctionComponent, useEffect, useState } from "react";
|
||||||
import * as Constants from "../../../Common/Constants";
|
import * as Constants from "../../../Common/Constants";
|
||||||
import { getErrorMessage, getErrorStack } from "../../../Common/ErrorHandlingUtils";
|
import { getErrorMessage, getErrorStack } from "../../../Common/ErrorHandlingUtils";
|
||||||
@ -204,8 +205,8 @@ export const AddDatabasePanel: FunctionComponent<AddDatabasePaneProps> = ({
|
|||||||
type="text"
|
type="text"
|
||||||
aria-required="true"
|
aria-required="true"
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
pattern="[^/?#\\]*[^/?# \\]"
|
pattern={ValidCosmosDbIdInputPattern.source}
|
||||||
title="May not end with space nor contain characters '\' '/' '#' '?'"
|
title={ValidCosmosDbIdDescription}
|
||||||
size={40}
|
size={40}
|
||||||
aria-label={databaseIdLabel}
|
aria-label={databaseIdLabel}
|
||||||
placeholder={databaseIdPlaceHolder}
|
placeholder={databaseIdPlaceHolder}
|
||||||
|
@ -39,7 +39,7 @@ exports[`AddDatabasePane Pane should render Default properly 1`] = `
|
|||||||
data-lpignore={true}
|
data-lpignore={true}
|
||||||
id="database-id"
|
id="database-id"
|
||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
pattern="[^/?#\\\\]*[^/?# \\\\]"
|
pattern="[^\\/?#\\\\]*[^\\/?# \\\\]"
|
||||||
placeholder="Type a new database id"
|
placeholder="Type a new database id"
|
||||||
size={40}
|
size={40}
|
||||||
styles={
|
styles={
|
||||||
|
@ -7,6 +7,7 @@ import { Action } from "Shared/Telemetry/TelemetryConstants";
|
|||||||
import * as TelemetryProcessor from "Shared/Telemetry/TelemetryProcessor";
|
import * as TelemetryProcessor from "Shared/Telemetry/TelemetryProcessor";
|
||||||
import { userContext } from "UserContext";
|
import { userContext } from "UserContext";
|
||||||
import { isServerlessAccount } from "Utils/CapabilityUtils";
|
import { isServerlessAccount } from "Utils/CapabilityUtils";
|
||||||
|
import { ValidCosmosDbIdDescription, ValidCosmosDbIdInputPattern } from "Utils/ValidationUtils";
|
||||||
import { useSidePanel } from "hooks/useSidePanel";
|
import { useSidePanel } from "hooks/useSidePanel";
|
||||||
import React, { FunctionComponent, useState } from "react";
|
import React, { FunctionComponent, useState } from "react";
|
||||||
import { ThroughputInput } from "../../Controls/ThroughputInput/ThroughputInput";
|
import { ThroughputInput } from "../../Controls/ThroughputInput/ThroughputInput";
|
||||||
@ -202,8 +203,8 @@ export const CassandraAddCollectionPane: FunctionComponent<CassandraAddCollectio
|
|||||||
required={true}
|
required={true}
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
styles={getTextFieldStyles()}
|
styles={getTextFieldStyles()}
|
||||||
pattern="[^/?#\\-]*[^/?#- \\]"
|
pattern={ValidCosmosDbIdInputPattern.source}
|
||||||
title="May not end with space nor contain characters '\' '/' '#' '?' '-'"
|
title={ValidCosmosDbIdDescription}
|
||||||
placeholder="Type a new keyspace id"
|
placeholder="Type a new keyspace id"
|
||||||
size={40}
|
size={40}
|
||||||
value={newKeyspaceId}
|
value={newKeyspaceId}
|
||||||
@ -292,8 +293,8 @@ export const CassandraAddCollectionPane: FunctionComponent<CassandraAddCollectio
|
|||||||
required={true}
|
required={true}
|
||||||
ariaLabel="addCollection-table Id Create table"
|
ariaLabel="addCollection-table Id Create table"
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
pattern="[^/?#\\-]*[^/?#- \\]"
|
pattern={ValidCosmosDbIdInputPattern.source}
|
||||||
title="May not end with space nor contain characters '\' '/' '#' '?' '-'"
|
title={ValidCosmosDbIdDescription}
|
||||||
placeholder="Enter table Id"
|
placeholder="Enter table Id"
|
||||||
size={20}
|
size={20}
|
||||||
value={tableId}
|
value={tableId}
|
||||||
|
@ -28,6 +28,7 @@ import { RightPaneForm } from "Explorer/Panes/RightPaneForm/RightPaneForm";
|
|||||||
import { useDatabases } from "Explorer/useDatabases";
|
import { useDatabases } from "Explorer/useDatabases";
|
||||||
import { userContext } from "UserContext";
|
import { userContext } from "UserContext";
|
||||||
import { getCollectionName } from "Utils/APITypeUtils";
|
import { getCollectionName } from "Utils/APITypeUtils";
|
||||||
|
import { ValidCosmosDbIdDescription, ValidCosmosDbIdInputPattern } from "Utils/ValidationUtils";
|
||||||
import { useSidePanel } from "hooks/useSidePanel";
|
import { useSidePanel } from "hooks/useSidePanel";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
@ -235,8 +236,8 @@ export const ChangePartitionKeyPane: React.FC<ChangePartitionKeyPaneProps> = ({
|
|||||||
aria-required
|
aria-required
|
||||||
required
|
required
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
pattern="[^/?#\\]*[^/?# \\]"
|
pattern={ValidCosmosDbIdInputPattern.source}
|
||||||
title="May not end with space nor contain characters '\' '/' '#' '?'"
|
title={ValidCosmosDbIdDescription}
|
||||||
placeholder={`e.g., ${getCollectionName()}1`}
|
placeholder={`e.g., ${getCollectionName()}1`}
|
||||||
size={40}
|
size={40}
|
||||||
className="panelTextField"
|
className="panelTextField"
|
||||||
|
@ -93,7 +93,7 @@ exports[`AddCollectionPanel should render Default properly 1`] = `
|
|||||||
id="newDatabaseId"
|
id="newDatabaseId"
|
||||||
name="newDatabaseId"
|
name="newDatabaseId"
|
||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
pattern="[^/?#\\\\]*[^/?# \\\\]"
|
pattern="[^\\/?#\\\\]*[^\\/?# \\\\]"
|
||||||
placeholder="Type a new database id"
|
placeholder="Type a new database id"
|
||||||
required={true}
|
required={true}
|
||||||
size={40}
|
size={40}
|
||||||
@ -178,7 +178,7 @@ exports[`AddCollectionPanel should render Default properly 1`] = `
|
|||||||
id="collectionId"
|
id="collectionId"
|
||||||
name="collectionId"
|
name="collectionId"
|
||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
pattern="[^/?#\\\\]*[^/?# \\\\]"
|
pattern="[^\\/?#\\\\]*[^\\/?# \\\\]"
|
||||||
placeholder="e.g., Container1"
|
placeholder="e.g., Container1"
|
||||||
required={true}
|
required={true}
|
||||||
size={40}
|
size={40}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import * as ko from "knockout";
|
import * as ko from "knockout";
|
||||||
import Q from "q";
|
import Q from "q";
|
||||||
|
import { IsValidCosmosDbResourceId } from "Utils/ValidationUtils";
|
||||||
import DiscardIcon from "../../../images/discard.svg";
|
import DiscardIcon from "../../../images/discard.svg";
|
||||||
import SaveIcon from "../../../images/save-cosmos.svg";
|
import SaveIcon from "../../../images/save-cosmos.svg";
|
||||||
import * as Constants from "../../Common/Constants";
|
import * as Constants from "../../Common/Constants";
|
||||||
@ -57,7 +58,7 @@ export default abstract class ScriptTabBase extends TabsBase implements ViewMode
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.id = editable.observable<string>();
|
this.id = editable.observable<string>();
|
||||||
this.id.validations([ScriptTabBase._isValidId]);
|
this.id.validations([IsValidCosmosDbResourceId]);
|
||||||
|
|
||||||
this.editorContent = editable.observable<string>();
|
this.editorContent = editable.observable<string>();
|
||||||
this.editorContent.validations([ScriptTabBase._isNotEmpty]);
|
this.editorContent.validations([ScriptTabBase._isNotEmpty]);
|
||||||
@ -262,29 +263,6 @@ export default abstract class ScriptTabBase extends TabsBase implements ViewMode
|
|||||||
this.updateNavbarWithTabsButtons();
|
this.updateNavbarWithTabsButtons();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static _isValidId(id: string): boolean {
|
|
||||||
if (!id) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const invalidStartCharacters = /^[/?#\\]/;
|
|
||||||
if (invalidStartCharacters.test(id)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const invalidMiddleCharacters = /^.+[/?#\\]/;
|
|
||||||
if (invalidMiddleCharacters.test(id)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const invalidEndCharacters = /.*[/?#\\ ]$/;
|
|
||||||
if (invalidEndCharacters.test(id)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static _isNotEmpty(value: string): boolean {
|
private static _isNotEmpty(value: string): boolean {
|
||||||
return !!value;
|
return !!value;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { Resource, StoredProcedureDefinition } from "@azure/cosmos";
|
import { Resource, StoredProcedureDefinition } from "@azure/cosmos";
|
||||||
import { Pivot, PivotItem } from "@fluentui/react";
|
import { Pivot, PivotItem } from "@fluentui/react";
|
||||||
import { KeyboardAction } from "KeyboardShortcuts";
|
import { KeyboardAction } from "KeyboardShortcuts";
|
||||||
|
import { ValidCosmosDbIdDescription, ValidCosmosDbIdInputPattern } from "Utils/ValidationUtils";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import ExecuteQueryIcon from "../../../../images/ExecuteQuery.svg";
|
import ExecuteQueryIcon from "../../../../images/ExecuteQuery.svg";
|
||||||
import DiscardIcon from "../../../../images/discard.svg";
|
import DiscardIcon from "../../../../images/discard.svg";
|
||||||
@ -455,11 +456,12 @@ export default class StoredProcedureTabComponent extends React.Component<
|
|||||||
}
|
}
|
||||||
|
|
||||||
public handleIdOnChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
public handleIdOnChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||||
|
const isValidId: boolean = event.currentTarget.reportValidity();
|
||||||
if (this.state.saveButton.visible) {
|
if (this.state.saveButton.visible) {
|
||||||
this.setState({
|
this.setState({
|
||||||
id: event.target.value,
|
id: event.target.value,
|
||||||
saveButton: {
|
saveButton: {
|
||||||
enabled: true,
|
enabled: isValidId,
|
||||||
visible: this.props.scriptTabBaseInstance.isNew(),
|
visible: this.props.scriptTabBaseInstance.isNew(),
|
||||||
},
|
},
|
||||||
discardButton: {
|
discardButton: {
|
||||||
@ -528,8 +530,8 @@ export default class StoredProcedureTabComponent extends React.Component<
|
|||||||
className="formTree"
|
className="formTree"
|
||||||
type="text"
|
type="text"
|
||||||
required
|
required
|
||||||
pattern="[^/?#\\]*[^/?# \\]"
|
pattern={ValidCosmosDbIdInputPattern.source}
|
||||||
title="May not end with space nor contain characters '\' '/' '#' '?'"
|
title={ValidCosmosDbIdDescription}
|
||||||
aria-label="Stored procedure id"
|
aria-label="Stored procedure id"
|
||||||
placeholder="Enter the new stored procedure id"
|
placeholder="Enter the new stored procedure id"
|
||||||
size={40}
|
size={40}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { TriggerDefinition } from "@azure/cosmos";
|
import { TriggerDefinition } from "@azure/cosmos";
|
||||||
import { Dropdown, IDropdownOption, Label, TextField } from "@fluentui/react";
|
import { Dropdown, IDropdownOption, Label, TextField } from "@fluentui/react";
|
||||||
import { KeyboardAction } from "KeyboardShortcuts";
|
import { KeyboardAction } from "KeyboardShortcuts";
|
||||||
|
import { ValidCosmosDbIdDescription, ValidCosmosDbIdInputPattern } from "Utils/ValidationUtils";
|
||||||
import React, { Component } from "react";
|
import React, { Component } from "react";
|
||||||
import DiscardIcon from "../../../images/discard.svg";
|
import DiscardIcon from "../../../images/discard.svg";
|
||||||
import SaveIcon from "../../../images/save-cosmos.svg";
|
import SaveIcon from "../../../images/save-cosmos.svg";
|
||||||
@ -192,29 +193,6 @@ export class TriggerTabContent extends Component<TriggerTab, ITriggerTabContentS
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private isValidId(id: string): boolean {
|
|
||||||
if (!id) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const invalidStartCharacters = /^[/?#\\]/;
|
|
||||||
if (invalidStartCharacters.test(id)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const invalidMiddleCharacters = /^.+[/?#\\]/;
|
|
||||||
if (invalidMiddleCharacters.test(id)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const invalidEndCharacters = /.*[/?#\\ ]$/;
|
|
||||||
if (invalidEndCharacters.test(id)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private isNotEmpty(value: string): boolean {
|
private isNotEmpty(value: string): boolean {
|
||||||
return !!value;
|
return !!value;
|
||||||
}
|
}
|
||||||
@ -286,7 +264,13 @@ export class TriggerTabContent extends Component<TriggerTab, ITriggerTabContentS
|
|||||||
_event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>,
|
_event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>,
|
||||||
newValue?: string,
|
newValue?: string,
|
||||||
): void => {
|
): void => {
|
||||||
this.saveButton.enabled = this.isValidId(newValue) && this.isNotEmpty(newValue);
|
const inputElement = _event.currentTarget as HTMLInputElement;
|
||||||
|
let isValidId: boolean = true;
|
||||||
|
if (inputElement) {
|
||||||
|
isValidId = inputElement.reportValidity();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.saveButton.enabled = this.isNotEmpty(newValue) && isValidId;
|
||||||
this.setState({ triggerId: newValue });
|
this.setState({ triggerId: newValue });
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -313,7 +297,8 @@ export class TriggerTabContent extends Component<TriggerTab, ITriggerTabContentS
|
|||||||
autoFocus
|
autoFocus
|
||||||
required
|
required
|
||||||
type="text"
|
type="text"
|
||||||
pattern="[^/?#\\]*[^/?# \\]"
|
pattern={ValidCosmosDbIdInputPattern.source}
|
||||||
|
title={ValidCosmosDbIdDescription}
|
||||||
placeholder="Enter the new trigger id"
|
placeholder="Enter the new trigger id"
|
||||||
size={40}
|
size={40}
|
||||||
value={triggerId}
|
value={triggerId}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { UserDefinedFunctionDefinition } from "@azure/cosmos";
|
import { UserDefinedFunctionDefinition } from "@azure/cosmos";
|
||||||
import { Label, TextField } from "@fluentui/react";
|
import { Label, TextField } from "@fluentui/react";
|
||||||
import { KeyboardAction } from "KeyboardShortcuts";
|
import { KeyboardAction } from "KeyboardShortcuts";
|
||||||
|
import { ValidCosmosDbIdDescription, ValidCosmosDbIdInputPattern } from "Utils/ValidationUtils";
|
||||||
import React, { Component } from "react";
|
import React, { Component } from "react";
|
||||||
import DiscardIcon from "../../../images/discard.svg";
|
import DiscardIcon from "../../../images/discard.svg";
|
||||||
import SaveIcon from "../../../images/save-cosmos.svg";
|
import SaveIcon from "../../../images/save-cosmos.svg";
|
||||||
@ -64,7 +65,13 @@ export default class UserDefinedFunctionTabContent extends Component<
|
|||||||
_event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>,
|
_event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>,
|
||||||
newValue?: string,
|
newValue?: string,
|
||||||
): void => {
|
): void => {
|
||||||
this.saveButton.enabled = this.isValidId(newValue) && this.isNotEmpty(newValue);
|
const inputElement = _event.currentTarget as HTMLInputElement;
|
||||||
|
let isValidId: boolean = true;
|
||||||
|
if (inputElement) {
|
||||||
|
isValidId = inputElement.reportValidity();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.saveButton.enabled = this.isNotEmpty(newValue) && isValidId;
|
||||||
this.setState({ udfId: newValue });
|
this.setState({ udfId: newValue });
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -238,29 +245,6 @@ export default class UserDefinedFunctionTabContent extends Component<
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private isValidId(id: string): boolean {
|
|
||||||
if (!id) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const invalidStartCharacters = /^[/?#\\]/;
|
|
||||||
if (invalidStartCharacters.test(id)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const invalidMiddleCharacters = /^.+[/?#\\]/;
|
|
||||||
if (invalidMiddleCharacters.test(id)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const invalidEndCharacters = /.*[/?#\\ ]$/;
|
|
||||||
if (invalidEndCharacters.test(id)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private isNotEmpty(value: string): boolean {
|
private isNotEmpty(value: string): boolean {
|
||||||
return !!value;
|
return !!value;
|
||||||
}
|
}
|
||||||
@ -284,7 +268,8 @@ export default class UserDefinedFunctionTabContent extends Component<
|
|||||||
required
|
required
|
||||||
readOnly={!isUdfIdEditable}
|
readOnly={!isUdfIdEditable}
|
||||||
type="text"
|
type="text"
|
||||||
pattern="[^/?#\\]*[^/?# \\]"
|
pattern={ValidCosmosDbIdInputPattern.source}
|
||||||
|
title={ValidCosmosDbIdDescription}
|
||||||
placeholder="Enter the new user defined function id"
|
placeholder="Enter the new user defined function id"
|
||||||
size={40}
|
size={40}
|
||||||
value={udfId}
|
value={udfId}
|
||||||
|
18
src/Utils/ValidationUtils.test.ts
Normal file
18
src/Utils/ValidationUtils.test.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { IsValidCosmosDbResourceId } from "Utils/ValidationUtils";
|
||||||
|
|
||||||
|
const testCases = [
|
||||||
|
["validId", true],
|
||||||
|
["forward/slash", false],
|
||||||
|
["back\\slash", false],
|
||||||
|
["question?mark", false],
|
||||||
|
["hash#mark", false],
|
||||||
|
["?invalidstart", false],
|
||||||
|
["invalidEnd/", false],
|
||||||
|
["space-at-end ", false],
|
||||||
|
];
|
||||||
|
|
||||||
|
describe("IsValidCosmosDbResourceId", () => {
|
||||||
|
test.each(testCases)("IsValidCosmosDbResourceId(%p). Expected: %p", (id: string, expected: boolean) => {
|
||||||
|
expect(IsValidCosmosDbResourceId(id)).toBe(expected);
|
||||||
|
});
|
||||||
|
});
|
24
src/Utils/ValidationUtils.ts
Normal file
24
src/Utils/ValidationUtils.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
//
|
||||||
|
// Common methods and constants for validation
|
||||||
|
//
|
||||||
|
|
||||||
|
//
|
||||||
|
// Validation of id for Cosmos DB resources:
|
||||||
|
// - Database
|
||||||
|
// - Container
|
||||||
|
// - Stored Procedure
|
||||||
|
// - User Defined Function (UDF)
|
||||||
|
// - Trigger
|
||||||
|
//
|
||||||
|
// Use these with <input> elements
|
||||||
|
// eslint-disable-next-line no-useless-escape
|
||||||
|
export const ValidCosmosDbIdInputPattern: RegExp = /[^\/?#\\]*[^\/?# \\]/;
|
||||||
|
export const ValidCosmosDbIdDescription: string = "May not end with space nor contain characters '\\' '/' '#' '?'";
|
||||||
|
|
||||||
|
// For a standalone function regex, we need to wrap the previous reg expression,
|
||||||
|
// to test against the entire value. This is done implicitly by input elements.
|
||||||
|
const ValidCosmosDbIdRegex: RegExp = new RegExp(`^(?:${ValidCosmosDbIdInputPattern.source})$`);
|
||||||
|
|
||||||
|
export function IsValidCosmosDbResourceId(id: string): boolean {
|
||||||
|
return id && ValidCosmosDbIdRegex.test(id);
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user