-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
0c672ef
commit 1e97692
Showing
6 changed files
with
357 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
export class EmailHandler { | ||
/** | ||
* Validate email | ||
* Reference: https://emailregex.com | ||
* @param email | ||
*/ | ||
static validate(email: string): boolean { | ||
return new RegExp(/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/).test(email); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import parsePhoneNumber from 'libphonenumber-js'; | ||
|
||
export class PhoneNumberHandler { | ||
static getValidPhoneNumber(phoneNumber: string): string { | ||
const parsedPhoneNumberInstance = parsePhoneNumber(phoneNumber, 'KR'); | ||
if (!parsedPhoneNumberInstance) { | ||
return phoneNumber; | ||
} | ||
|
||
if (!parsedPhoneNumberInstance.isValid() || !parsedPhoneNumberInstance.isPossible()) { | ||
return phoneNumber; | ||
} | ||
|
||
return parsedPhoneNumberInstance.number; | ||
} | ||
|
||
static getLocalPhoneNumber(phoneNumber: string, enableHyphen: boolean = true): string { | ||
const parsedPhoneNumberInstance = parsePhoneNumber(phoneNumber, 'KR'); | ||
if (!parsedPhoneNumberInstance) { | ||
return phoneNumber; | ||
} | ||
|
||
return enableHyphen ? parsedPhoneNumberInstance.formatNational() : parsedPhoneNumberInstance.formatNational().replaceAll('-', ''); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
export class Snowflake { | ||
static EPOCH = Date.UTC(1970, 0, 1).valueOf(); | ||
static SHARD_ID = 1; | ||
static SEQUENCE = 1; | ||
|
||
static generate({ timestamp = Date.now(), shard_id = Snowflake.SHARD_ID }: { timestamp?: Date | number; shard_id?: number; } = {}): string { | ||
if (timestamp instanceof Date) { | ||
timestamp = timestamp.valueOf(); | ||
} else { | ||
timestamp = new Date(timestamp).valueOf(); | ||
} | ||
|
||
let result = (BigInt(timestamp) - BigInt(Snowflake.EPOCH)) << BigInt(22); | ||
result = result | (BigInt(shard_id % 1024) << BigInt(12)); | ||
result = result | BigInt(Snowflake.SEQUENCE++ % 4096); | ||
|
||
return result.toString(); | ||
} | ||
|
||
static parse(snowflake: SnowflakeResolvable): DeconstructedSnowflake { | ||
const binary = Snowflake.binary(snowflake); | ||
|
||
return { | ||
timestamp: Snowflake.extractBits(snowflake, 1, 41), | ||
shard_id: Snowflake.extractBits(snowflake, 42, 10), | ||
sequence: Snowflake.extractBits(snowflake, 52), | ||
binary, | ||
}; | ||
} | ||
|
||
static isValid(snowflake: string): boolean { | ||
if (!/^\d{19}$/.test(snowflake)) { | ||
return false; | ||
} | ||
|
||
try { | ||
Snowflake.parse(snowflake); | ||
return true; | ||
} catch (e) { | ||
return false; | ||
} | ||
} | ||
|
||
static extractBits(snowflake: SnowflakeResolvable, start: number, length?: number): number { | ||
return parseInt( | ||
length | ||
? Snowflake.binary(snowflake).substring(start, start + length) | ||
: Snowflake.binary(snowflake).substring(start), | ||
2, | ||
); | ||
} | ||
|
||
static binary(snowflake: SnowflakeResolvable): string { | ||
const cached64BitZeros = '0000000000000000000000000000000000000000000000000000000000000000'; | ||
const binValue = BigInt(snowflake).toString(2); | ||
|
||
return binValue.length < 64 | ||
? cached64BitZeros.substring(0, 64 - binValue.length) + binValue | ||
: binValue; | ||
} | ||
} | ||
|
||
type SnowflakeResolvable = string; | ||
|
||
interface DeconstructedSnowflake { | ||
timestamp: number; | ||
shard_id: number; | ||
sequence: number; | ||
binary: string; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { EmailHandler } from '../EmailHandler'; | ||
|
||
describe('EmailHandler', () => { | ||
describe('validate', () => { | ||
it('should return true when email is valid', () => { | ||
expect(EmailHandler.validate('steve@example.com')).toBe(true); | ||
}); | ||
|
||
it('should return false when email is invalid', () => { | ||
expect(EmailHandler.validate('steve@example')).toBe(false); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
import { PhoneNumberHandler } from '../PhoneNumberHandler'; | ||
|
||
describe('PhoneNumberHandler', () => { | ||
describe('getValidPhoneNumber', () => { | ||
it('should return given parameter if phoneNumber is invalid', () => { | ||
const undefinedPhoneNumberString = ''; | ||
|
||
const phoneNumber = PhoneNumberHandler.getValidPhoneNumber(undefinedPhoneNumberString); | ||
|
||
expect(phoneNumber).toBe(''); | ||
}); | ||
|
||
it('should return given parameter if phoneNumber is unavailable', () => { | ||
const impossiblePhoneNumberString = '000000000'; | ||
|
||
const phoneNumber = PhoneNumberHandler.getValidPhoneNumber(impossiblePhoneNumberString); | ||
|
||
expect(phoneNumber).toBe('000000000') | ||
}); | ||
|
||
describe('should return valid phoneNumber', () => { | ||
it('if given phoneNumber is common mobile phone number', () => { | ||
const phoneNumberString = '01012345678'; | ||
const phoneNumberStringWithHyphen = '010-1234-5678'; | ||
const phoneNumberStringWithSpace = '010 1234 5678'; | ||
const phoneNumberStringWithParenthesis = '(010)1234-5678'; | ||
const phoneNumberStringWithCountryCode = '+821012345678'; | ||
const phoneNumberStringWithCountryCodeAndHyphen = '+82-10-1234-5678'; | ||
const phoneNumberStringWithCountryCodeAndParenthesis = '+82(10)1234-5678'; | ||
const phoneNumberStringWithCountryCodeAndHyphenAndParenthesis = '+82-(10)1234-5678'; | ||
|
||
const phoneNumber = PhoneNumberHandler.getValidPhoneNumber(phoneNumberString); | ||
const phoneNumberWithHyphen = PhoneNumberHandler.getValidPhoneNumber(phoneNumberStringWithHyphen); | ||
const phoneNumberWithSpace = PhoneNumberHandler.getValidPhoneNumber(phoneNumberStringWithSpace); | ||
const phoneNumberWithParenthesis = PhoneNumberHandler.getValidPhoneNumber(phoneNumberStringWithParenthesis); | ||
const phoneNumberWithCountryCode = PhoneNumberHandler.getValidPhoneNumber(phoneNumberStringWithCountryCode); | ||
const phoneNumberWithCountryCodeAndHyphen = PhoneNumberHandler.getValidPhoneNumber(phoneNumberStringWithCountryCodeAndHyphen); | ||
const phoneNumberWithCountryCodeAndParenthesis = PhoneNumberHandler.getValidPhoneNumber(phoneNumberStringWithCountryCodeAndParenthesis); | ||
const phoneNumberWithCountryCodeAndHyphenAndParenthesis = PhoneNumberHandler.getValidPhoneNumber(phoneNumberStringWithCountryCodeAndHyphenAndParenthesis); | ||
|
||
expect(phoneNumber).toBe('+821012345678'); | ||
expect(phoneNumberWithHyphen).toBe('+821012345678'); | ||
expect(phoneNumberWithSpace).toBe('+821012345678'); | ||
expect(phoneNumberWithParenthesis).toBe('+821012345678'); | ||
expect(phoneNumberWithCountryCode).toBe('+821012345678'); | ||
expect(phoneNumberWithCountryCodeAndHyphen).toBe('+821012345678'); | ||
expect(phoneNumberWithCountryCodeAndParenthesis).toBe('+821012345678'); | ||
expect(phoneNumberWithCountryCodeAndHyphenAndParenthesis).toBe('+821012345678'); | ||
}); | ||
|
||
it('if given phoneNumber is common landline phone number', () => { | ||
const phoneNumberString = '0311234567'; | ||
const phoneNumberStringWithHyphen = '031-123-4567'; | ||
const phoneNumberStringWithSpace = '031 123 4567'; | ||
const phoneNumberStringWithParenthesis = '(031)123-4567'; | ||
const phoneNumberStringWithCountryCode = '+82311234567'; | ||
const phoneNumberStringWithCountryCodeAndHyphen = '+82-31-123-4567'; | ||
const phoneNumberStringWithCountryCodeAndParenthesis = '+82(31)123-4567'; | ||
const phoneNumberStringWithCountryCodeAndHyphenAndParenthesis = '+82-(31)123-4567'; | ||
|
||
const phoneNumber = PhoneNumberHandler.getValidPhoneNumber(phoneNumberString); | ||
const phoneNumberWithHyphen = PhoneNumberHandler.getValidPhoneNumber(phoneNumberStringWithHyphen); | ||
const phoneNumberWithSpace = PhoneNumberHandler.getValidPhoneNumber(phoneNumberStringWithSpace); | ||
const phoneNumberWithParenthesis = PhoneNumberHandler.getValidPhoneNumber(phoneNumberStringWithParenthesis); | ||
const phoneNumberWithCountryCode = PhoneNumberHandler.getValidPhoneNumber(phoneNumberStringWithCountryCode); | ||
const phoneNumberWithCountryCodeAndHyphen = PhoneNumberHandler.getValidPhoneNumber(phoneNumberStringWithCountryCodeAndHyphen); | ||
const phoneNumberWithCountryCodeAndParenthesis = PhoneNumberHandler.getValidPhoneNumber(phoneNumberStringWithCountryCodeAndParenthesis); | ||
const phoneNumberWithCountryCodeAndHyphenAndParenthesis = PhoneNumberHandler.getValidPhoneNumber(phoneNumberStringWithCountryCodeAndHyphenAndParenthesis); | ||
|
||
expect(phoneNumber).toBe('+82311234567'); | ||
expect(phoneNumberWithHyphen).toBe('+82311234567'); | ||
expect(phoneNumberWithSpace).toBe('+82311234567'); | ||
expect(phoneNumberWithParenthesis).toBe('+82311234567'); | ||
expect(phoneNumberWithCountryCode).toBe('+82311234567'); | ||
expect(phoneNumberWithCountryCodeAndHyphen).toBe('+82311234567'); | ||
expect(phoneNumberWithCountryCodeAndParenthesis).toBe('+82311234567'); | ||
expect(phoneNumberWithCountryCodeAndHyphenAndParenthesis).toBe('+82311234567'); | ||
}); | ||
|
||
it('if given phoneNumber is 070 number', () => { | ||
const phoneNumberString = '07012345678'; | ||
const phoneNumberStringWithHyphen = '070-1234-5678'; | ||
const phoneNumberStringWithSpace = '070 1234 5678'; | ||
const phoneNumberStringWithParenthesis = '(070)1234-5678'; | ||
const phoneNumberStringWithCountryCode = '+827012345678'; | ||
const phoneNumberStringWithCountryCodeAndHyphen = '+82-70-1234-5678'; | ||
const phoneNumberStringWithCountryCodeAndParenthesis = '+82(70)1234-5678'; | ||
const phoneNumberStringWithCountryCodeAndHyphenAndParenthesis = '+82-(70)1234-5678'; | ||
|
||
const phoneNumber = PhoneNumberHandler.getValidPhoneNumber(phoneNumberString); | ||
const phoneNumberWithHyphen = PhoneNumberHandler.getValidPhoneNumber(phoneNumberStringWithHyphen); | ||
const phoneNumberWithSpace = PhoneNumberHandler.getValidPhoneNumber(phoneNumberStringWithSpace); | ||
const phoneNumberWithParenthesis = PhoneNumberHandler.getValidPhoneNumber(phoneNumberStringWithParenthesis); | ||
const phoneNumberWithCountryCode = PhoneNumberHandler.getValidPhoneNumber(phoneNumberStringWithCountryCode); | ||
const phoneNumberWithCountryCodeAndHyphen = PhoneNumberHandler.getValidPhoneNumber(phoneNumberStringWithCountryCodeAndHyphen); | ||
const phoneNumberWithCountryCodeAndParenthesis = PhoneNumberHandler.getValidPhoneNumber(phoneNumberStringWithCountryCodeAndParenthesis); | ||
const phoneNumberWithCountryCodeAndHyphenAndParenthesis = PhoneNumberHandler.getValidPhoneNumber(phoneNumberStringWithCountryCodeAndHyphenAndParenthesis); | ||
|
||
expect(phoneNumber).toBe('+827012345678'); | ||
expect(phoneNumberWithHyphen).toBe('+827012345678'); | ||
expect(phoneNumberWithSpace).toBe('+827012345678'); | ||
expect(phoneNumberWithParenthesis).toBe('+827012345678'); | ||
expect(phoneNumberWithCountryCode).toBe('+827012345678'); | ||
expect(phoneNumberWithCountryCodeAndHyphen).toBe('+827012345678'); | ||
expect(phoneNumberWithCountryCodeAndParenthesis).toBe('+827012345678'); | ||
expect(phoneNumberWithCountryCodeAndHyphenAndParenthesis).toBe('+827012345678'); | ||
}); | ||
|
||
it('if given phoneNumber is XXXX-XXXX number', () => { | ||
const phoneNumberString = '1234-5678'; | ||
const phoneNumberStringWithHyphen = '1234-5678'; | ||
const phoneNumberStringWithSpace = '1234 5678'; | ||
const phoneNumberStringWithParenthesis = '(1234)5678'; | ||
const phoneNumberStringWithCountryCode = '+8212345678'; | ||
const phoneNumberStringWithCountryCodeAndHyphen = '+82-1234-5678'; | ||
const phoneNumberStringWithCountryCodeAndParenthesis = '+82(1234)5678'; | ||
const phoneNumberStringWithCountryCodeAndHyphenAndParenthesis = '+82-(1234)5678'; | ||
|
||
const phoneNumber = PhoneNumberHandler.getValidPhoneNumber(phoneNumberString); | ||
const phoneNumberWithHyphen = PhoneNumberHandler.getValidPhoneNumber(phoneNumberStringWithHyphen); | ||
const phoneNumberWithSpace = PhoneNumberHandler.getValidPhoneNumber(phoneNumberStringWithSpace); | ||
const phoneNumberWithParenthesis = PhoneNumberHandler.getValidPhoneNumber(phoneNumberStringWithParenthesis); | ||
const phoneNumberWithCountryCode = PhoneNumberHandler.getValidPhoneNumber(phoneNumberStringWithCountryCode); | ||
const phoneNumberWithCountryCodeAndHyphen = PhoneNumberHandler.getValidPhoneNumber(phoneNumberStringWithCountryCodeAndHyphen); | ||
const phoneNumberWithCountryCodeAndParenthesis = PhoneNumberHandler.getValidPhoneNumber(phoneNumberStringWithCountryCodeAndParenthesis); | ||
const phoneNumberWithCountryCodeAndHyphenAndParenthesis = PhoneNumberHandler.getValidPhoneNumber(phoneNumberStringWithCountryCodeAndHyphenAndParenthesis); | ||
|
||
expect(phoneNumber).toBe('+8212345678'); | ||
expect(phoneNumberWithHyphen).toBe('+8212345678'); | ||
expect(phoneNumberWithSpace).toBe('+8212345678'); | ||
expect(phoneNumberWithParenthesis).toBe('+8212345678'); | ||
expect(phoneNumberWithCountryCode).toBe('+8212345678'); | ||
expect(phoneNumberWithCountryCodeAndHyphen).toBe('+8212345678'); | ||
expect(phoneNumberWithCountryCodeAndParenthesis).toBe('+8212345678'); | ||
expect(phoneNumberWithCountryCodeAndHyphenAndParenthesis).toBe('+8212345678'); | ||
}); | ||
}); | ||
}); | ||
|
||
describe('getLocalPhoneNumber', () => { | ||
it('should return local phone number', () => { | ||
expect(PhoneNumberHandler.getLocalPhoneNumber('+821012345678')).toBe('010-1234-5678'); | ||
expect(PhoneNumberHandler.getLocalPhoneNumber('+82311234567')).toBe('031-123-4567'); | ||
expect(PhoneNumberHandler.getLocalPhoneNumber('+827012345678')).toBe('070-1234-5678'); | ||
expect(PhoneNumberHandler.getLocalPhoneNumber('+8212345678')).toBe('1234-5678'); | ||
expect(PhoneNumberHandler.getLocalPhoneNumber('+8221234567')).toBe('02-123-4567'); | ||
}); | ||
|
||
it('should return local phone number with hyphen', () => { | ||
expect(PhoneNumberHandler.getLocalPhoneNumber('+821012345678', false)).toBe('01012345678'); | ||
expect(PhoneNumberHandler.getLocalPhoneNumber('+82311234567', false)).toBe('0311234567'); | ||
expect(PhoneNumberHandler.getLocalPhoneNumber('+827012345678', false)).toBe('07012345678'); | ||
expect(PhoneNumberHandler.getLocalPhoneNumber('+8212345678', false)).toBe('12345678'); | ||
expect(PhoneNumberHandler.getLocalPhoneNumber('+8221234567', false)).toBe('021234567'); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
import { Snowflake } from '../Snowflake'; | ||
|
||
const tests = [ | ||
{ | ||
timestamp: 1653653263221, | ||
value: '6935924496540897281', | ||
shard_id: 1, | ||
sequence: 1, | ||
}, | ||
{ | ||
timestamp: 1653653289600, | ||
value: '6935924607185511855', | ||
shard_id: 750, | ||
sequence: 1455, | ||
}, | ||
{ | ||
timestamp: 1653653311149, | ||
value: '6935924697568571351', | ||
shard_id: 750, | ||
sequence: 4055, | ||
}, | ||
]; | ||
|
||
describe('Snowflake', () => { | ||
beforeEach(() => { | ||
Snowflake.SEQUENCE = 1; | ||
}); | ||
|
||
describe('generate', () => { | ||
it('should generate a snowflake', () => { | ||
for (const test of tests) { | ||
Snowflake.SEQUENCE = test.sequence; | ||
expect( | ||
Snowflake.generate({ | ||
timestamp: test.timestamp, | ||
shard_id: test.shard_id, | ||
}).toString() | ||
).toEqual(test.value); | ||
} | ||
}); | ||
|
||
it('should generate a random snowflake', () => { | ||
const generated: string[] = []; | ||
for (const test of [...Array(5)]) { | ||
generated.push(Snowflake.generate()); | ||
} | ||
expect(generated.length).toEqual(new Set(generated).size); | ||
}); | ||
|
||
it('should generate a unique snowflake', () => { | ||
const generated: string[] = []; | ||
for (const test of [...Array(1e6)]) { | ||
generated.push(Snowflake.generate()); | ||
} | ||
expect(generated.length).toEqual(new Set(generated).size); | ||
}); | ||
}); | ||
|
||
describe('deconstruct', () => { | ||
it('should deconstruct a snowflake', () => { | ||
for (const test of tests) { | ||
const parsed = Snowflake.parse(test.value); | ||
expect(parsed.timestamp).toEqual(test.timestamp); | ||
expect(parsed.sequence).toEqual(test.sequence); | ||
expect(parsed.shard_id).toEqual(test.shard_id); | ||
} | ||
}); | ||
}); | ||
|
||
describe('isValid', () => { | ||
it('should deconstruct a snowflake', () => { | ||
const tests = [ | ||
{ value: '6917082698162902015', valid: true }, | ||
{ value: 'abv', valid: false }, | ||
]; | ||
for (const test of tests) { | ||
const parsed = Snowflake.isValid(test.value); | ||
expect(parsed).toEqual(test.valid); | ||
} | ||
}); | ||
}); | ||
}); |