Skip to content

Commit

Permalink
chore(#6741): use antlr parser for fqn and entityLink
Browse files Browse the repository at this point in the history
  • Loading branch information
Sachin-chaurasiya committed Jul 6, 2023
1 parent f8ff645 commit f67389b
Show file tree
Hide file tree
Showing 4 changed files with 242 additions and 32 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright 2023 Collate.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import EntityLinkListener from '../generated/antlr/EntityLinkListener';

export default class EntityLinkSplitListener extends EntityLinkListener {
constructor() {
super();
this.entityLinkParts = [];
}

// Enter a parse tree produced by EntityLinkParser#entityType.
enterEntityType(ctx) {
this.entityLinkParts.push(ctx.getText());
}

// Enter a parse tree produced by EntityLinkParser#entityAttribute.
enterEntityAttribute(ctx) {
this.entityLinkParts.push(ctx.getText());
}

// Enter a parse tree produced by EntityLinkParser#entityFqn.
enterEntityFqn(ctx) {
this.entityLinkParts.push(ctx.getText());
}

// Enter a parse tree produced by EntityLinkParser#entityField.
enterEntityField(ctx) {
this.entityLinkParts.push(ctx.getText());
}

split() {
return this.entityLinkParts;
}
}
111 changes: 111 additions & 0 deletions openmetadata-ui/src/main/resources/ui/src/utils/EntityLink.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*
* Copyright 2023 Collate.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import antlr4 from 'antlr4';
import { ParseTreeWalker } from 'antlr4/src/antlr4/tree';
import EntityLinkSplitListener from '../antlr/EntityLinkSplitListener';
import EntityLinkLexer from '../generated/antlr/EntityLinkLexer';
import EntityLinkParser from '../generated/antlr/EntityLinkParser';
import { ENTITY_LINK_SEPARATOR } from './EntityUtils';

export default class EntityLink {
/**
*
* @param string entityLink
* @returns list of entity link parts
*/
static split(entityLink) {
const chars = new antlr4.InputStream(entityLink);
const lexer = new EntityLinkLexer(chars);
const tokens = new antlr4.CommonTokenStream(lexer);
const parser = new EntityLinkParser(tokens);
const tree = parser.entitylink();
const splitter = new EntityLinkSplitListener();
ParseTreeWalker.DEFAULT.walk(splitter, tree);

return splitter.split();
}

/**
*
* @param string entityLink
* @returns entity type
*/
static getEntityType(entityLink) {
return EntityLink.split(entityLink)[0];
}

/**
*
* @param string entityLink
* @returns entity fqn
*/
static getEntityFqn(entityLink) {
return EntityLink.split(entityLink)[1];
}

/**
*
* @param string entityLink
* @returns entity field
*/
static getEntityField(entityLink) {
const entityType = EntityLink.getEntityType(entityLink);
if (entityType === 'table') {
return EntityLink.split(entityLink)[2];
}

return EntityLink.split(entityLink)[-1];
}

/**
*
* @param string entityLink
* @returns column name for table entity
*/
static getTableColumnName(entityLink) {
return EntityLink.split(entityLink)[3];
}

/**
*
* @param string entityLink
* @returns column field for table entity
*/
static getTableColumnField(entityLink) {
return EntityLink.split(entityLink)[4];
}

/**
*
* @param string tableFqn
* @param string | undefined columnName
* @returns entity link for table
*/
static getTableEntityLink(tableFqn, columnName) {
if (columnName) {
return `<#E${ENTITY_LINK_SEPARATOR}table${ENTITY_LINK_SEPARATOR}${tableFqn}${ENTITY_LINK_SEPARATOR}columns${ENTITY_LINK_SEPARATOR}${columnName}>`;
} else {
return `<#E${ENTITY_LINK_SEPARATOR}table${ENTITY_LINK_SEPARATOR}${tableFqn}>`;
}
}

/**
*
* @param string entityType
* @param string entityFqn
* @returns entityLink
*/
static getEntityLink(entityType, entityFqn) {
return `<#E${ENTITY_LINK_SEPARATOR}${entityType}${ENTITY_LINK_SEPARATOR}${entityFqn}>`;
}
}
84 changes: 84 additions & 0 deletions openmetadata-ui/src/main/resources/ui/src/utils/EntityLink.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* Copyright 2023 Collate.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import EntityLink from './EntityLink';

const entityLink =
'<#E::table::sample_data.ecommerce_db.shopify.dim_address::description>';
const entityLinkWithColumn =
'<#E::table::sample_data.ecommerce_db.shopify.dim_address::columns::address_id::tags>';

describe('Test EntityLink', () => {
it('Should split the entityLink into parts', () => {
const entityLinkPartsWithColumn = EntityLink.split(entityLinkWithColumn);
const entityLinkParts = EntityLink.split(entityLink);

expect(entityLinkParts).toStrictEqual([
'table',
'sample_data.ecommerce_db.shopify.dim_address',
'description',
]);

expect(entityLinkPartsWithColumn).toStrictEqual([
'table',
'sample_data.ecommerce_db.shopify.dim_address',
'columns',
'address_id',
'tags',
]);
});

it('Should return the entityType from entityLink', () => {
expect(EntityLink.getEntityType(entityLink)).toStrictEqual('table');
});

it('Should return the entityFqn from entityLink', () => {
expect(EntityLink.getEntityFqn(entityLink)).toStrictEqual(
'sample_data.ecommerce_db.shopify.dim_address'
);
});

it('Should return the entityField from entityLink', () => {
expect(EntityLink.getEntityField(entityLink)).toStrictEqual('description');
});

it('Should return the columnName from entityLink', () => {
expect(EntityLink.getTableColumnName(entityLinkWithColumn)).toStrictEqual(
'address_id'
);
});

it('Should return the column field from entityLink', () => {
expect(EntityLink.getTableColumnField(entityLinkWithColumn)).toStrictEqual(
'tags'
);
});

it('Should return the undefined if columnName if not present in entityLink', () => {
expect(EntityLink.getTableColumnName(entityLink)).toBeUndefined();
});

it('Should return the undefined if columnField if not present in entityLink', () => {
expect(EntityLink.getTableColumnField(entityLink)).toBeUndefined();
});

it('Should build the entityLink', () => {
expect(
EntityLink.getEntityLink(
'table',
'sample_data.ecommerce_db.shopify.dim_address'
)
).toStrictEqual(
'<#E::table::sample_data.ecommerce_db.shopify.dim_address>'
);
});
});
35 changes: 3 additions & 32 deletions openmetadata-ui/src/main/resources/ui/src/utils/FeedUtils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,21 +59,18 @@ import {
getPartialNameFromFQN,
getPartialNameFromTableFQN,
} from './CommonUtils';
import EntityLink from './EntityLink';
import { ENTITY_LINK_SEPARATOR } from './EntityUtils';
import { getEncodedFqn } from './StringsUtils';
import { getEntityLink } from './TableUtils';
import { getRelativeDateByTimeStamp } from './TimeUtils';
import { showErrorToast } from './ToastUtils';

export const getEntityType = (entityLink: string) => {
const match = EntityRegEx.exec(entityLink);

return match?.[1] as EntityType;
return EntityLink.getEntityType(entityLink);
};
export const getEntityFQN = (entityLink: string) => {
const match = EntityRegEx.exec(entityLink);

return match?.[2];
return EntityLink.getEntityFqn(entityLink);
};
export const getEntityField = (entityLink: string) => {
const match = EntityRegEx.exec(entityLink);
Expand Down Expand Up @@ -155,23 +152,6 @@ export const getThreadField = (
return value.split(separator).slice(-2);
};

export const getThreadValue = (
columnName: string,
columnField: string,
entityFieldThreads: EntityFieldThreads[]
) => {
let threadValue;

entityFieldThreads?.forEach((thread) => {
const threadField = getThreadField(thread.entityField);
if (threadField[0] === columnName && threadField[1] === columnField) {
threadValue = thread;
}
});

return threadValue;
};

export const buildMentionLink = (entityType: string, entityFqn: string) => {
return `${document.location.protocol}//${document.location.host}/${entityType}/${entityFqn}`;
};
Expand Down Expand Up @@ -484,21 +464,12 @@ export const updateThreadData = (
}
};

export const getFeedAction = (type: ThreadType) => {
if (type === ThreadType.Task) {
return i18next.t('label.created-a-task-lowercase');
}

return i18next.t('label.posted-on-lowercase');
};

export const prepareFeedLink = (entityType: string, entityFQN: string) => {
const withoutFeedEntities = [
EntityType.WEBHOOK,
EntityType.GLOSSARY,
EntityType.GLOSSARY_TERM,
EntityType.TYPE,
EntityType.MLMODEL,
];

const entityLink = getEntityLink(entityType, entityFQN);
Expand Down

0 comments on commit f67389b

Please sign in to comment.