mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-09-06 23:52:29 +00:00
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:
parent
a5fafb1289
commit
b6d5a438ec
@ -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
|
||||
|
@ -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");
|
||||
|
@ -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'.",
|
||||
|
@ -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',
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
@ -123,7 +123,7 @@ const GlossaryHeader = ({
|
||||
const handleGlossaryImport = () =>
|
||||
history.push(
|
||||
getGlossaryPathWithAction(
|
||||
selectedData.fullyQualifiedName ?? '',
|
||||
encodeURIComponent(selectedData.fullyQualifiedName ?? ''),
|
||||
EntityAction.IMPORT
|
||||
)
|
||||
);
|
||||
|
@ -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]}`);
|
||||
}
|
||||
}}>
|
||||
|
@ -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;
|
||||
|
@ -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) => {
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
);
|
||||
|
@ -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) {
|
||||
|
@ -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"
|
||||
|
Loading…
x
Reference in New Issue
Block a user