Support '%' in entity Name (#12382)

* added support for the % in entity name

* added support for the % in glossary and glossary term  name

* Added U exclude

* Fixed pytest

* fix: support % in glossary name

* added suppport for % in entityName

* fix % issue in the glossary or term

* fix: glossary with % encoding

* fix: tags redirect issue for glossaries with %

* fix: glossary import path

* fixed CI

* fixed Py Test

* fixed PyTest

* fixed glossary resource test

---------

Co-authored-by: Onkar Ravgan <onkar.10r@gmail.com>
Co-authored-by: karanh37 <karanh37@gmail.com>
Co-authored-by: Chirag Madlani <12962843+chirag-madlani@users.noreply.github.com>
Co-authored-by: karanh37 <33024356+karanh37@users.noreply.github.com>
This commit is contained in:
07Himank 2023-07-18 17:58:16 +05:30 committed by GitHub
parent a5fafb1289
commit b6d5a438ec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 73 additions and 49 deletions

View File

@ -341,7 +341,9 @@ public class GlossaryResourceTest extends EntityResourceTest<Glossary, CreateGlo
String csv = createCsv(GlossaryCsv.HEADERS, listOf(record), null);
CsvImportResult result = importCsv(glossaryName, csv, false);
assertSummary(result, CsvImportResult.Status.FAILURE, 2, 1, 1);
String[] expectedRows = {resultsHeader, getFailedRecord(record, "[name must match \"\"^(?U)[\\w'\\- .&()]+$\"\"]")};
String[] expectedRows = {
resultsHeader, getFailedRecord(record, "[name must match \"\"^(?U)[\\w'\\- .&()%]+$\"\"]")
};
assertRows(result, expectedRows);
// Create glossaryTerm with invalid parent

View File

@ -22,7 +22,7 @@ class ValidatorUtilTest {
// Invalid name
glossary.withName("invalid::Name").withDescription("description");
assertEquals("[name must match \"^(?U)[\\w'\\- .&()]+$\"]", ValidatorUtil.validate(glossary));
assertEquals("[name must match \"^(?U)[\\w'\\- .&()%]+$\"]", ValidatorUtil.validate(glossary));
// No error
glossary.withName("validName").withId(UUID.randomUUID()).withDescription("description");

View File

@ -100,7 +100,7 @@
"type": "string",
"minLength": 1,
"maxLength": 128,
"pattern": "^(?U)[\\w'\\- .&()]+$"
"pattern": "^(?U)[\\w'\\- .&()%]+$"
},
"fullyQualifiedEntityName": {
"description": "A unique name that identifies an entity. Example for table 'DatabaseService:Database:Table'.",

View File

@ -240,8 +240,8 @@ export const NEW_GLOSSARY = {
tag: 'PII.None',
};
export const NEW_GLOSSARY_1 = {
name: 'Product Glossary',
description: 'This is the Product glossary',
name: 'Product%Glossary',
description: 'This is the Product glossary with percentage',
reviewer: 'Brandy Miller',
tag: 'PII.None',
};
@ -273,16 +273,16 @@ export const GLOSSARY_TERM_WITH_DETAILS = {
export const NEW_GLOSSARY_1_TERMS = {
term_1: {
name: 'Features',
name: 'Features%Term',
description: 'This is the Features',
synonyms: 'data,collect,time',
fullyQualifiedName: 'Product Glossary.Features',
fullyQualifiedName: 'Product%Glossary.Features%Term',
},
term_2: {
name: 'Uses',
description: 'This is the Uses',
synonyms: 'home,business,adventure',
fullyQualifiedName: 'Product Glossary.Uses',
fullyQualifiedName: 'Product%Glossary.Uses',
},
};

View File

@ -46,6 +46,7 @@
"diff": "^5.0.0",
"fast-json-patch": "^3.1.1",
"fs-extra": "^10.1.0",
"history": "4.5.1",
"html-react-parser": "^1.2.6",
"https-browserify": "^1.0.0",
"i18next": "^21.10.0",
@ -223,5 +224,8 @@
"webpack-cli": "4.10.0",
"webpack-dev-server": "4.7.4",
"webpackbar": "5.0.2"
},
"resolutions": {
"history": "4.5.1"
}
}

View File

@ -123,7 +123,7 @@ const GlossaryHeader = ({
const handleGlossaryImport = () =>
history.push(
getGlossaryPathWithAction(
selectedData.fullyQualifiedName ?? '',
encodeURIComponent(selectedData.fullyQualifiedName ?? ''),
EntityAction.IMPORT
)
);

View File

@ -92,7 +92,9 @@ const Tags: FunctionComponent<TagProps> = ({
onClick={() => {
if (tag.source && startWith !== TAG_START_WITH.PLUS) {
tag.source === TagSource.Glossary
? history.push(`${ROUTES.GLOSSARY}/${tag.tagFQN}`)
? history.push(
`${ROUTES.GLOSSARY}/${encodeURIComponent(tag.tagFQN)}`
)
: history.push(`${ROUTES.TAGS}/${tag.tagFQN.split('.')[0]}`);
}
}}>

View File

@ -24,7 +24,7 @@ export const FQN_REGEX = new RegExp(
* strings that contain a combination of letters, alphanumeric characters, hyphens,
* spaces, periods, single quotes, ampersands, and parentheses, with support for Unicode characters.
*/
export const ENTITY_NAME_REGEX = /^[\p{L}\w\- .'&()]+$/u;
export const ENTITY_NAME_REGEX = /^[\p{L}\w\- .'&()%]+$/u;
export const delimiterRegex = /[\\[\]\\()\\;\\,\\|\\{}\\``\\/\\<>\\^]/g;
export const nameWithSpace = /\s/g;

View File

@ -34,6 +34,7 @@ const GlossaryLeftPanel = ({ glossaries }: GlossaryLeftPanelProps) => {
const { t } = useTranslation();
const { permissions } = usePermissionProvider();
const { glossaryName } = useParams<{ glossaryName: string }>();
const glossaryFqn = glossaryName ? decodeURIComponent(glossaryName) : null;
const history = useHistory();
const createGlossaryPermission = useMemo(
@ -42,12 +43,12 @@ const GlossaryLeftPanel = ({ glossaries }: GlossaryLeftPanelProps) => {
[permissions]
);
const selectedKey = useMemo(() => {
if (glossaryName) {
return Fqn.split(glossaryName)[0];
if (glossaryFqn) {
return Fqn.split(glossaryFqn)[0];
}
return glossaries[0].name;
}, [glossaryName]);
}, [glossaryFqn]);
const menuItems: ItemType[] = useMemo(() => {
return glossaries.reduce((acc, glossary) => {

View File

@ -48,7 +48,8 @@ import GlossaryLeftPanel from '../GlossaryLeftPanel/GlossaryLeftPanel.component'
const GlossaryPage = () => {
const { t } = useTranslation();
const { permissions } = usePermissionProvider();
const { glossaryName: glossaryFqn } = useParams<{ glossaryName: string }>();
const { glossaryName } = useParams<{ glossaryName: string }>();
const glossaryFqn = decodeURIComponent(glossaryName);
const history = useHistory();
const [glossaries, setGlossaries] = useState<Glossary[]>([]);
const [isLoading, setIsLoading] = useState(true);

View File

@ -135,7 +135,7 @@ export const getGlossaryTermByFQN = async (
arrQueryFields: string | string[] = ''
) => {
const url = getURLWithQueryFields(
`/glossaryTerms/name/${glossaryTermFQN}`,
`/glossaryTerms/name/${encodeURIComponent(glossaryTermFQN)}`,
arrQueryFields
);
@ -193,7 +193,9 @@ export const importGlossaryInCSVFormat = async (
headers: { 'Content-type': 'text/plain' },
};
const response = await APIClient.put<string, AxiosResponse<CSVImportResult>>(
`/glossaries/name/${glossaryName}/import?dryRun=${dryRun}`,
`/glossaries/name/${encodeURIComponent(
glossaryName
)}/import?dryRun=${dryRun}`,
data,
configOptions
);

View File

@ -139,7 +139,7 @@ export const getGlossaryPath = (fqn?: string) => {
let path = ROUTES.GLOSSARY;
if (fqn) {
path = ROUTES.GLOSSARY_DETAILS;
path = path.replace(PLACEHOLDER_GLOSSARY_NAME, fqn);
path = path.replace(PLACEHOLDER_GLOSSARY_NAME, encodeURIComponent(fqn));
}
return path;
@ -456,7 +456,7 @@ export const getGlossaryTermsVersionsPath = (
? ROUTES.GLOSSARY_TERMS_VERSION_TAB
: ROUTES.GLOSSARY_TERMS_VERSION;
path = path
.replace(PLACEHOLDER_GLOSSARY_NAME, glossaryTermsFQN)
.replace(PLACEHOLDER_GLOSSARY_NAME, encodeURIComponent(glossaryTermsFQN))
.replace(PLACEHOLDER_ROUTE_VERSION, version);
if (tab) {

View File

@ -2078,6 +2078,13 @@
dependencies:
regenerator-runtime "^0.13.11"
"@babel/runtime@^7.12.13":
version "7.22.6"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.6.tgz#57d64b9ae3cff1d67eb067ae117dac087f5bd438"
integrity sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ==
dependencies:
regenerator-runtime "^0.13.11"
"@babel/runtime@^7.14.5", "@babel/runtime@^7.17.2", "@babel/runtime@^7.19.0":
version "7.19.0"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.19.0.tgz#22b11c037b094d27a8a2504ea4dcff00f50e2259"
@ -8075,17 +8082,16 @@ he@^1.2.0:
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
history@^4.9.0:
version "4.10.1"
resolved "https://registry.yarnpkg.com/history/-/history-4.10.1.tgz#33371a65e3a83b267434e2b3f3b1b4c58aad4cf3"
integrity sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==
history@4.5.1, history@^4.9.0:
version "4.5.1"
resolved "https://registry.yarnpkg.com/history/-/history-4.5.1.tgz#44935a51021e3b8e67ebac267a35675732aba569"
integrity sha512-gfHeJeYeMzFtos61gdA1AloO0hGXPF2Yum+2FRdJvlylYQOz51OnT1zuwg9UYst1BRrONhcAh3Nmsg9iblgl6g==
dependencies:
"@babel/runtime" "^7.1.2"
invariant "^2.2.1"
loose-envify "^1.2.0"
resolve-pathname "^3.0.0"
tiny-invariant "^1.0.2"
tiny-warning "^1.0.0"
value-equal "^1.0.1"
resolve-pathname "^2.0.0"
value-equal "^0.2.0"
warning "^3.0.0"
hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2:
version "3.3.2"
@ -8456,7 +8462,7 @@ interpret@^2.2.0:
resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9"
integrity sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==
invariant@^2.2.4:
invariant@^2.2.1, invariant@^2.2.4:
version "2.2.4"
resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==
@ -10112,7 +10118,7 @@ min-indent@^1.0.0:
resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869"
integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==
mini-create-react-context@^0.4.0, mini-create-react-context@^0.4.1:
mini-create-react-context@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz#072171561bfdc922da08a60c2197a497cc2d1d5e"
integrity sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ==
@ -12253,28 +12259,27 @@ react-resize-detector@^8.0.4:
lodash "^4.17.21"
react-router-dom@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.2.0.tgz#9e65a4d0c45e13289e66c7b17c7e175d0ea15662"
integrity sha512-gxAmfylo2QUjcwxI63RhQ5G85Qqt4voZpUXSEqCwykV0baaOTQDR1f0PmY8AELqIyVc0NEZUj0Gov5lNGcXgsA==
version "5.3.4"
resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.3.4.tgz#2ed62ffd88cae6db134445f4a0c0ae8b91d2e5e6"
integrity sha512-m4EqFMHv/Ih4kpcBCONHbkT68KoAeHN4p3lAGoNryfHi0dMy0kCzEZakiKRsvg5wHZ/JLrLW8o8KomWiz/qbYQ==
dependencies:
"@babel/runtime" "^7.1.2"
"@babel/runtime" "^7.12.13"
history "^4.9.0"
loose-envify "^1.3.1"
prop-types "^15.6.2"
react-router "5.2.0"
react-router "5.3.4"
tiny-invariant "^1.0.2"
tiny-warning "^1.0.0"
react-router@5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/react-router/-/react-router-5.2.0.tgz#424e75641ca8747fbf76e5ecca69781aa37ea293"
integrity sha512-smz1DUuFHRKdcJC0jobGo8cVbhO3x50tCL4icacOlcwDOEQPq4TMqwx3sY1TP+DvtTgz4nm3thuo7A+BK2U0Dw==
react-router@5.3.4:
version "5.3.4"
resolved "https://registry.yarnpkg.com/react-router/-/react-router-5.3.4.tgz#8ca252d70fcc37841e31473c7a151cf777887bb5"
integrity sha512-Ys9K+ppnJah3QuaRiLxk+jDWOR1MekYQrlytiXxC1RyfbdsZkS5pvKAzCCr031xHixZwpnsYNT5xysdFHQaYsA==
dependencies:
"@babel/runtime" "^7.1.2"
"@babel/runtime" "^7.12.13"
history "^4.9.0"
hoist-non-react-statics "^3.1.0"
loose-envify "^1.3.1"
mini-create-react-context "^0.4.0"
path-to-regexp "^1.7.0"
prop-types "^15.6.2"
react-is "^16.6.0"
@ -12709,10 +12714,10 @@ resolve-from@^5.0.0:
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69"
integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==
resolve-pathname@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-3.0.0.tgz#99d02224d3cf263689becbb393bc560313025dcd"
integrity sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==
resolve-pathname@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-2.2.0.tgz#7e9ae21ed815fd63ab189adeee64dc831eefa879"
integrity sha512-bAFz9ld18RzJfddgrO2e/0S2O81710++chRMUxHjXOYKF6jTAMrUNZrEZ1PvV0zlhfjidm08iRPdTLPno1FuRg==
resolve-url@^0.2.1:
version "0.2.1"
@ -14516,10 +14521,10 @@ validate.io-number@^1.0.3:
resolved "https://registry.yarnpkg.com/validate.io-number/-/validate.io-number-1.0.3.tgz#f63ffeda248bf28a67a8d48e0e3b461a1665baf8"
integrity sha1-9j/+2iSL8opnqNSODjtGGhZluvg=
value-equal@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-1.0.1.tgz#1e0b794c734c5c0cade179c437d356d931a34d6c"
integrity sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==
value-equal@^0.2.0:
version "0.2.1"
resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-0.2.1.tgz#c220a304361fce6994dbbedaa3c7e1a1b895871d"
integrity sha512-yRL36Xb2K/HmFT5Fe3M86S7mu4+a12/3l7uytUh6eNPPjP77ldPBvsAvmnWff39sXn55naRMZN8LZWRO8PWaeQ==
vary@~1.1.2:
version "1.1.2"
@ -14591,6 +14596,13 @@ walker@^1.0.7, walker@~1.0.5:
dependencies:
makeerror "1.0.x"
warning@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/warning/-/warning-3.0.0.tgz#32e5377cb572de4ab04753bdf8821c01ed605b7c"
integrity sha512-jMBt6pUrKn5I+OGgtQ4YZLdhIeJmObddh6CsibPxyQ5yPZm1XExSyzC1LCNX7BzhxWgiHmizBWJTHJIjMjTQYQ==
dependencies:
loose-envify "^1.0.0"
watchpack@^2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d"