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(Utility): expose UUID generator #303

Merged
merged 2 commits into from
May 28, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions ngx-tools/src/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ isArray([]); // Returns: true
- [`isString`](#isstring)
- [`arrayHasAllElementsSet`](#arrayhasallelementsset)
- [`VERSION`](#version)
- [UUID](#uuid)

<!-- END doctoc generated TOC please keep comment here to allow auto update -->

Expand Down Expand Up @@ -885,6 +886,18 @@ VERSION.patch // Returns: 3
```


### UUID

[[source]](uuid/uuid.ts)

Generate a canonically formatted UUID that is Version 1 through 5 and is the appropriate Variant as per RFC4122.

```typescript
import { generateUUID } from '@terminus/ngx-tools';

generateUUID(); // Returns a UUID such as: `f4ee5eed-ed19-3681-713e-907a23ed7858`
```



<!-- LINKS -->
Expand Down
4 changes: 4 additions & 0 deletions ngx-tools/src/jwt-token-managment/effects.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ describe(`JWT Token Effects`, function() {
};

test(`should provide a cookie and store cookie message if the cookie is set`, () => {
// tslint:disable-next-line deprecation
Copy link
Contributor

Choose a reason for hiding this comment

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

What is getting deprecated, here, 118, 136, and 155?

const currentState = of<State>(blankState);
mockCookieService.get.mockReturnValue('abcd');

Expand All @@ -114,6 +115,7 @@ describe(`JWT Token Effects`, function() {
});

test(`should emit nothing if state is loaded`, () => {
// tslint:disable-next-line deprecation
const currentState = of<State>({
...blankState,
jwtTokens: {
Expand All @@ -131,6 +133,7 @@ describe(`JWT Token Effects`, function() {
});

test(`should emit nothing if state is empty`, () => {
// tslint:disable-next-line deprecation
const currentState = of<State>({
...blankState,
jwtTokens: {
Expand All @@ -149,6 +152,7 @@ describe(`JWT Token Effects`, function() {
});

it(`it should only announce the initial if the cookie is empty`, () => {
// tslint:disable-next-line deprecation
const currentState = of<State>(blankState);
mockCookieService.get.mockReturnValue('');

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ describe(`TokenEscalator`, function() {
let escalator: TokenEscalator<MockClaimMap>;
let actions: Observable<any>;
const tokenName = 'foo';
// tslint:disable-next-line deprecation
const authorizeUrl = of('/foobar');

beforeEach(() => {
Expand Down Expand Up @@ -56,7 +57,9 @@ describe(`TokenEscalator`, function() {
test(`should dispatch success on a successful response`, () => {
actions = cold('a', {a: new JwtActions.EscalateToken<ClaimMap>(tokenName)});
const responseBody = {token: 'asdfkjlslfd'};
// tslint:disable-next-line deprecation
mockHttp.get.mockReturnValue(of(responseBody));
// tslint:disable-next-line deprecation
mockStore.select.mockReturnValue(of('currentToken'));

(
Expand All @@ -74,7 +77,9 @@ describe(`TokenEscalator`, function() {
test(`should dispatch failed if the token fails to extract`, () => {
actions = cold('a', {a: new JwtActions.EscalateToken<ClaimMap>(tokenName)});
const responseBody = {};
// tslint:disable-next-line deprecation
mockHttp.get.mockReturnValue(of(responseBody));
// tslint:disable-next-line deprecation
mockStore.select.mockReturnValue(of('currentToken'));

(
Expand Down
1 change: 1 addition & 0 deletions ngx-tools/src/public-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,4 @@ export * from './update-control-on-input-changes/update-control-on-input-changes
export * from './verify-types/index';
export * from './version/version';
export * from './window/window.service';
export * from './uuid/uuid';
3 changes: 3 additions & 0 deletions ngx-tools/src/retry-with-backoff/retry-with-backoff.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ describe(`retryWithBackoff`, function() {
const error = new Error('bar');
const seenValues: {[idx: number]: number} = {};

// tslint:disable-next-line deprecation
Copy link
Contributor

Choose a reason for hiding this comment

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

Okay, so I will assume of is getting deprecated.
Do we have a plan of updating our code?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We're actually just getting around a TSLint bug. The deprecation rule is triggered even though of itself is not deprecated; only certain overloads are. We did this a bit here also GetTerminus/terminus-ui#1488

There is an open PR to fix this which would allow us to remove all of these disables. But since TSLint itself is deprecated I'm not holding my breath on it getting fixed.

of(1, 2, 3).pipe(
tap(v => {
if (!seenValues[v]) {
Expand All @@ -33,6 +34,7 @@ describe(`retryWithBackoff`, function() {
retries: 2,
delayCalculator: linearBackoff,
}),
// tslint:disable-next-line no-any
).subscribe(() => {}, (err: any) => {
expect(err).toEqual(error);
expect(seenValues).toEqual({
Expand All @@ -48,6 +50,7 @@ describe(`retryWithBackoff`, function() {
const error = new Error('bar');
const seenValues: {[idx: number]: number} = {};

// tslint:disable-next-line deprecation
of(1, 2, 3, 4).pipe(
tap(v => {
if (!seenValues[v]) {
Expand Down
37 changes: 37 additions & 0 deletions ngx-tools/src/uuid/uuid.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { uuidRegex } from '@terminus/ngx-tools/regex';

import { generateUUID } from './uuid';


describe(`uuid`, function() {
const testUUIDGenerator = function(generator: () => string, iterations = 1, done: jest.DoneCallback): Promise<void> {
const uuidstore: Record<string, string> = {};
let i;
let newUuid;

for (i = 0; i < iterations; i++) {
newUuid = generator();

// Test Validity
if (!uuidRegex.test(newUuid)) {
done.fail(new Error(`Not a valid UUID: ${newUuid}`));
}

// Test Collision
if (uuidstore[newUuid]) {
done.fail(new Error(`Collision on ${newUuid}`));
}
uuidstore[newUuid] = newUuid;
}

return Promise.resolve();
};


test(`should create UUIDs that do not collide`, function(done) {
testUUIDGenerator(generateUUID, 100, done).then(() => {
done();
});
});

});
23 changes: 23 additions & 0 deletions ngx-tools/src/uuid/uuid.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/* eslint-disable no-magic-numbers */
/**
* Generate a canonically formatted UUID that is Version 1 through 5 and is the appropriate Variant as per RFC4122.
*
* @example
* generateUUID() // Returns: `f4ee5eed-ed19-3681-713e-907a23ed7858`
*
* @return The UUID
*/
export function generateUUID(): string {
const buf = new Uint16Array(8);
window.crypto.getRandomValues(buf);

const S4 = function(num: number) {
let ret = num.toString(16);
while (ret.length < 4) {
ret = `0${ret}`;
}
return ret;
};

return (`${S4(buf[0]) + S4(buf[1])}-${S4(buf[2])}-${S4(buf[3])}-${S4(buf[4])}-${S4(buf[5])}${S4(buf[6])}${S4(buf[7])}`);
}
1 change: 0 additions & 1 deletion ngx-tools/testing/src/utilities/event-objects.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ describe(`event-objects`, function() {
const actual: KeyboardEvent = createKeyboardEvent('keydown', KEYS.ENTER, target);
expect(actual.code).toEqual(KEYS.ENTER.code);
expect(actual.key).toEqual(KEYS.ENTER.code);
expect(actual.keyCode).toEqual(KEYS.ENTER.keyCode);
expect(actual.target).toEqual(target);
});

Expand Down
1 change: 0 additions & 1 deletion ngx-tools/testing/src/utilities/event-objects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@ export function createKeyboardEvent(
// Webkit Browsers don't set the keyCode when calling the init function.
// See related bug https://bugs.webkit.org/show_bug.cgi?id=16735
Object.defineProperties(event, {
keyCode: {get: () => key.keyCode},
key: {get: () => key.code},
target: {get: () => target},
code: {get: () => key.code},
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,8 @@
"tsickle": "^0.34.0",
"tslint": "^5.16.0",
"typescript": "~3.2.1",
"validate-commit-msg": "^2.14.0"
"validate-commit-msg": "^2.14.0",
"window-crypto": "^1.1.0"
Copy link
Contributor

Choose a reason for hiding this comment

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

Do you need this to use window.crypto.getRandomValues?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Only for testing. Jest doesn't have a polyfill for window.crypto by default. So in order to test it as accurately as possible, I'm just polyfilling the functionality as a whole (vs just mocking it).

},
"husky": {
"hooks": {
Expand Down
37 changes: 18 additions & 19 deletions tools/jest-global-mocks.ts
Original file line number Diff line number Diff line change
@@ -1,42 +1,42 @@
// Polyfill `window.crypto`
import 'window-crypto';

// tslint:disable no-any no-unsafe-any
const mock = () => {
let storage: Record<string, any> = {};
return {
getItem: (key: string): any => key in storage ? storage[key] : null,
getItem: (key: string): any => (key in storage ? storage[key] : null),
setItem: (key: string, value: any) => {
storage[key] = value || '';
return storage[key];
},
removeItem: (key: string) => delete storage[key],
clear: () => storage = {},
clear: () => (storage = {}),
Copy link
Contributor

Choose a reason for hiding this comment

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

Are the parenthesis needed or for better readability?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Kind of. Creating an assignment in a return statement can be confusing so we have a lint rule warning about this.

The () is just us being explicit that something is happening inside, then it returns.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

A better way for me to phrase it: It is not more readable, but the intent should be more clear.

};
};
// tslint:enable no-any no-unsafe-any

Object.defineProperty(window, 'localStorage', {value: mock()});
Object.defineProperty(window, 'sessionStorage', {value: mock()});
Object.defineProperty(window, 'getComputedStyle', {
value: () => ['-webkit-appearance'],
});
Object.defineProperty(window, 'getComputedStyle', {value: () => ['-webkit-appearance']});
Object.defineProperty(window, 'CSS', {value: () => ({})});




/**
* Patches for Material
*/
const WARN_SUPPRESSING_PATTERNS = [
/Could not find Angular Material core theme/,
/Could not find HammerJS/,
/Could not find Angular Material core theme/,
/Could not find HammerJS/,
];
// eslint-disable-next-line no-console
const warn = console.warn;
Object.defineProperty(console, 'warn', {
value: (...params: string[]) => {
if (!WARN_SUPPRESSING_PATTERNS.some((pattern) => pattern.test(params[0]))) {
warn(...params);
}
},
value: (...params: string[]) => {
if (!WARN_SUPPRESSING_PATTERNS.some(pattern => pattern.test(params[0]))) {
warn(...params);
}
},
});
Object.defineProperty(window, 'matchMedia', {
value: () => (
Expand All @@ -48,9 +48,8 @@ Object.defineProperty(window, 'matchMedia', {
),
});
Object.defineProperty(document.body.style, 'transform', {
value: () =>
({
enumerable: true,
configurable: true,
}),
value: () => ({
enumerable: true,
configurable: true,
}),
});
1 change: 1 addition & 0 deletions tslint.spec.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"extends": "@terminus/tslint-config-frontend/testing",
"rules" : {
"no-implicit-dependencies": false,
"no-non-null-assertion": false,
"prefer-on-push-component-change-detection": false
}
}
13 changes: 9 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -474,10 +474,10 @@
"@typescript-eslint/eslint-plugin-tslint" "^1.4.2"
"@typescript-eslint/parser" "^1.4.2"

"@terminus/ngx-tools@*":
version "6.7.0"
resolved "https://registry.yarnpkg.com/@terminus/ngx-tools/-/ngx-tools-6.7.0.tgz#6a5bcc41b9d8802669f3e030d3de835da2d77a17"
integrity sha512-bytDD9w6Rl6WilBRfvUwnp76HIB/U6LKYcycHJRlA3Fs98QRkSXgyPuh3uhN/yGxBB67JEGs78w9Uuxe79dcXg==
"@terminus/ngx-tools@^6.10.0":
version "6.10.0"
resolved "https://registry.yarnpkg.com/@terminus/ngx-tools/-/ngx-tools-6.10.0.tgz#d19da4e78ca489c189c8576f9db981cb0f7ce735"
integrity sha512-qxXupnVfj84gxSzk2AV/GvpS2EmGaA78mNiMlwhaY3s/CwB/3Bfe1+zCrhVBRzgyPUk0VDRcK3OSGng/efPHUQ==
dependencies:
tslib "^1.9.0"

Expand Down Expand Up @@ -12847,6 +12847,11 @@ widest-line@^2.0.0:
dependencies:
string-width "^2.1.1"

window-crypto@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/window-crypto/-/window-crypto-1.1.0.tgz#1f4acfe4ddacabfb5b4bd6eda7d2fe63e4e0b288"
integrity sha512-KD5ucryy9RGf9Yb68ynEFTXv1DVxAjqn9FmTlFTW+G38rbMmkJ92+iEEmwMoNZShdy9K6PT6s3uE7imyiXcFWA==

windows-release@^3.1.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/windows-release/-/windows-release-3.2.0.tgz#8122dad5afc303d833422380680a79cdfa91785f"
Expand Down