Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Support Wildcard and * contract filters #567

Merged
15 changes: 15 additions & 0 deletions frontend/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// jest.config.js
Kevin101Zhang marked this conversation as resolved.
Show resolved Hide resolved
module.exports = {
testEnvironment: 'node',
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1',
},
extensionsToTreatAsEsm: ['.jsx'],
transform: {
'^.+\\.[jt]sx?$': 'babel-jest',
},
testPathIgnorePatterns: [
'/formatters\\.test\\.js',
'/Editor\\.test\\.js',
],
};
9 changes: 9 additions & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"private": true,
"scripts": {
"dev": "next dev",
"test": "jest --watch --config jest.config.js",
"serve:widgets": "bos-loader dataplatform.near --path widgets/src -r replacement.mainnet.json",
"serve:widgets:local": "bos-loader dataplatform.near --path widgets/src -r replacement.local.json",
"serve:widgets:dev": "bos-loader dev-queryapi.dataplatform.near --path widgets/src -r replacement.dev.json",
Expand All @@ -25,6 +26,7 @@
"buffer": "^6.0.3",
"graphiql": "3.0.6",
"graphql": "^16.8.1",
"graphql-ws": "^5.15.0",
"gridjs": "6.0.6",
"monaco-editor": "^0.45.0",
"near-api-js": "1.1.0",
Expand All @@ -44,11 +46,18 @@
"use-debounce": "^10.0.0"
},
"devDependencies": {
"@babel/core": "^7.23.9",
"@babel/plugin-transform-modules-commonjs": "^7.23.3",
"@babel/preset-env": "^7.23.9",
"@babel/preset-react": "^7.23.3",
"@babel/register": "^7.23.7",
"autoprefixer": "^10.4.17",
"babel-jest": "^29.7.0",
"eslint": "8.50.0",
"eslint-config-next": "13.5.3",
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-react-hooks": "^4.6.0",
"jest": "^29.7.0",
"postcss": "^8.4.33",
"tailwindcss": "^3.4.1",
"typescript": "4.9.5"
Expand Down
226 changes: 226 additions & 0 deletions frontend/src/components/Editor/__tests__/validator.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
const CONTRACT_NAME_REGEX = RegExp(/^(([a-z\d]+[-_])*[a-z\d]+(\.([a-z\d]+[-_])*[a-z\d]+)*\.([a-z\d]+)|([a-z\d]+))$/);
darunrs marked this conversation as resolved.
Show resolved Hide resolved
const WILD_CARD_REGEX = RegExp(/\*\./);
// const INVALID_ACCOUNT = 'system';

function validateContractId(accountId) {
Kevin101Zhang marked this conversation as resolved.
Show resolved Hide resolved
accountId = accountId.trim();
const isLengthValid = accountId.length >= 2 && accountId.length <= 64;
Kevin101Zhang marked this conversation as resolved.
Show resolved Hide resolved
if (!isLengthValid) return false;

//test if the string starts with a '*.' and remove it if it does
const isWildCard = WILD_CARD_REGEX.test(accountId);
darunrs marked this conversation as resolved.
Show resolved Hide resolved
isWildCard ? accountId = accountId.slice(2) : null;

const isRegexValid = CONTRACT_NAME_REGEX.test(accountId);
return isRegexValid;
}

function validateContractIds(accountIds) {
const ids = accountIds.split(',').map(id => id.trim());
return ids.every(accountId => validateContractId(accountId));
}

describe('validateContractId', () => {
Kevin101Zhang marked this conversation as resolved.
Show resolved Hide resolved
test('it should return true for valid contract ID', () => {
const validId = 'contract1.near';
expect(validateContractId(validId)).toBe(true);
});

test('it should return true for wildcard contract ID', () => {
const wildcardId = '*.near';
expect(validateContractId(wildcardId)).toBe(true);
});

test('it should return false for empty string', () => {
const emptyString = '';
expect(validateContractId(emptyString)).toBe(false);
});

test('it should return false for invalid contract ID', () => {
const invalidId = 'invalid$contract';
expect(validateContractId(invalidId)).toBe(false);
});

test('it should return false for too short contract ID', () => {
const shortId = 'c';
expect(validateContractId(shortId)).toBe(false);
});

test('it should return false for too long contract ID', () => {
const longId = 'a'.repeat(65);
expect(validateContractId(longId)).toBe(false);
});

test('it should return true for contract ID with leading or trailing spaces', () => {
const spacedId = '*.kaching ';
expect(validateContractId(spacedId)).toBe(true);
});

test('it should return false for contract ID with consecutive dots', () => {
const dotId = 'contract..near';
expect(validateContractId(dotId)).toBe(false);
});

test('it should return false for contract ID with star in the middle characters', () => {
const invalidAsteriskOrder = 'contract.*.near';
expect(validateContractId(invalidAsteriskOrder)).toBe(false);
});

test('it should return false for contract ID with asterisk in center of string characters', () => {
const invalidAsteriskPosition = 'contract*2.near';
expect(validateContractId(invalidAsteriskPosition)).toBe(false);
});

test('it should return false for double asterisk in string', () => {
const multipleAsteriskOrder = '**.near';
expect(validateContractId(multipleAsteriskOrder)).toBe(false);
});

test('it should return false for double . in string', () => {
const invalidDotPosition = '*..near';
expect(validateContractId(invalidDotPosition)).toBe(false);
});

test('it should return false for contract ID starting with a dot', () => {
const dotStartId = '.near';
expect(validateContractId(dotStartId)).toBe(false);
});

test('it should return false for contract ID ending with a dot', () => {
const dotEndId = 'contract.near.';
expect(validateContractId(dotEndId)).toBe(false);
});

test('it should return false for contract ID ending with underscore or hyphen', () => {
const underscoreEndId = 'contract.near_';
const hyphenEndId = 'contract.near-';
expect(validateContractId(underscoreEndId)).toBe(false);
expect(validateContractId(hyphenEndId)).toBe(false);
});

//test on nomicon - https://nomicon.io/DataStructures/Account
test('it should return false for string with whitespace characters', () => {
const invalidWhitespace = 'not ok';
expect(validateContractId(invalidWhitespace)).toBe(false);
});

test('it should return false for string that is too short', () => {
const tooShort = 'a';
expect(validateContractId(tooShort)).toBe(false);
});

test('it should return false for string with suffix separator', () => {
const suffixSeparator = '100-';
expect(validateContractId(suffixSeparator)).toBe(false);
});

test('it should return false for string with consecutive separators', () => {
const consecutiveSeparators = 'bo__wen';
expect(validateContractId(consecutiveSeparators)).toBe(false);
});

test('it should return false for string with prefix separator', () => {
const prefixSeparator = '_illia';
expect(validateContractId(prefixSeparator)).toBe(false);
});

test('it should return false for string with prefix dot separator', () => {
const prefixDotSeparator = '.near';
expect(validateContractId(prefixDotSeparator)).toBe(false);
});

test('it should return false for string with suffix dot separator', () => {
const suffixDotSeparator = 'near.';
expect(validateContractId(suffixDotSeparator)).toBe(false);
});

test('it should return false for string with two dot separators in a row', () => {
const twoDotSeparators = 'a..near';
expect(validateContractId(twoDotSeparators)).toBe(false);
});

test('it should return false for string with non-alphanumeric characters', () => {
const nonAlphanumeric = '$$$';
expect(validateContractId(nonAlphanumeric)).toBe(false);
});

test('it should return false for string with non-lowercase characters', () => {
const nonLowercase = 'WAT';
expect(validateContractId(nonLowercase)).toBe(false);
});

test('it should return false for string with @ character', () => {
const invalidAtCharacter = '[email protected]';
expect(validateContractId(invalidAtCharacter)).toBe(false);
});

// not sure if this is valid
// test('it should return false for system account', () => {
// const systemAccount = 'system';
// expect(validateContractId(systemAccount)).toBe(false);
// });

test('it should return false for string that is too long', () => {
const tooLong = 'abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz';
expect(validateContractId(tooLong)).toBe(false);
});


});

describe('validateContractIds', () => {
test('it should return true for valid contract IDs', () => {
const validIds = 'contract1.near, contract2.near, contract3.near';
expect(validateContractIds(validIds)).toBe(true);
});

test('it should return true for wildcard contract ID in a list', () => {
const mixedIds = 'contract1.near, *.kaching, contract3.near';
expect(validateContractIds(mixedIds)).toBe(true);
});

test('it should return false for an empty string', () => {
const emptyString = '';
expect(validateContractIds(emptyString)).toBe(false);
});

test('it should return false for invalid contract IDs', () => {
const invalidIds = 'invalid$contract, 123, contract with space';
expect(validateContractIds(invalidIds)).toBe(false);
});

test('it should return false for a single invalid contract ID in the list', () => {
const mixedIds = 'contract1, invalid$contract, contract3';
expect(validateContractIds(mixedIds)).toBe(false);
});

test('it should return false for a mix of valid and invalid contract IDs', () => {
const mixedIds = 'contract1.near, invalid$contract, contract3.near';
expect(validateContractIds(mixedIds)).toBe(false);
});

test('it should return false for a mix of valid and invalid contract IDs with spaces', () => {
const spacedIds = 'contract1.near, invalid$contract, contract3.near ';
expect(validateContractIds(spacedIds)).toBe(false);
});

test('it should return true for a mix of valid and wildcard contract IDs', () => {
const mixedIds = 'contract1.near, *.near, contract3.near';
expect(validateContractIds(mixedIds)).toBe(true);
});

test('it should return false for an invalid wildcard contract ID where the wildcard is in the string', () => {
const invalidWildcard = '*invalid.near';
darunrs marked this conversation as resolved.
Show resolved Hide resolved
expect(validateContractIds(invalidWildcard)).toBe(false);
});

test('it should return false for an invalid wildcard contract ID followed by valid contractIDs', () => {
const invalidWildcardWithOthers = '*invalid.near, contract1.near, *.near';
expect(validateContractIds(invalidWildcardWithOthers)).toBe(false);
});

test('it should return false for an valid wildcard contract ID followed by invalid contractIDs', () => {
const validWildCardwithInvalid = '*.invalid.near, *contract1.near, *.near';
expect(validateContractIds(validWildCardwithInvalid)).toBe(false);
});
});
3 changes: 2 additions & 1 deletion frontend/src/constants/RegexExp.js
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export const CONTRACT_NAME_REGEX = RegExp(/^(\*|([a-z\d]+[-_])*[a-z\d]+)(\.*(\*|([a-z\d]+[-_])*[a-z\d]+))*\.(\w+)$/);
export const CONTRACT_NAME_REGEX = RegExp(/^(([a-z\d]+[-_])*[a-z\d]+(\.([a-z\d]+[-_])*[a-z\d]+)*\.([a-z\d]+)|([a-z\d]+))$/);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice job on the regex!

export const WILD_CARD_REGEX = RegExp(/\*\./);
20 changes: 13 additions & 7 deletions frontend/src/utils/validators.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
import { defaultSchema, formatIndexingCode, formatSQL } from "./formatters";
import { PgSchemaTypeGen } from "./pgSchemaTypeGen";
import { CONTRACT_NAME_REGEX } from '../constants/RegexExp';
import { CONTRACT_NAME_REGEX, WILD_CARD_REGEX } from '../constants/RegexExp';
import { ValidationError } from '../classes/ValidationError';
import { FORMATTING_ERROR_TYPE, TYPE_GENERATION_ERROR_TYPE } from "@/constants/Strings";

export function validateContractId(accountId) {
return (
accountId.length >= 2 &&
accountId.length <= 64 &&
CONTRACT_NAME_REGEX.test(accountId)
);
function validateContractId(accountId) {
accountId = accountId.trim();
const isLengthValid = accountId.length >= 2 && accountId.length <= 64;
if (!isLengthValid) return false;

//test if the string starts with a '*.' and remove it if it does
const isWildCard = WILD_CARD_REGEX.test(accountId);
isWildCard ? accountId = accountId.slice(2) : null;
darunrs marked this conversation as resolved.
Show resolved Hide resolved

//test if rest of string is valid accounting for/not isWildCard
const isRegexValid = CONTRACT_NAME_REGEX.test(accountId);
return isRegexValid;
}

export function validateContractIds(accountIds) {
Expand Down
Loading