From 1156a9f9b825ac2f26f2d474a9a55d8cd685a09a Mon Sep 17 00:00:00 2001 From: Diogo Bastos <50139916+DiogoABastos@users.noreply.github.com> Date: Wed, 28 Sep 2022 10:33:03 +0100 Subject: [PATCH] [AAE-10668] - Fix [object Object] for License entitlements in Admin App's About page (#7862) * [AAE-10668] Fixed [object object] issue for the entitlements field of the license section in the about page * Added string manipulation functionality * Removed lock file from commit * [AAE-10668] resolved changes requested * [AAE-10668] changes to ObjectUtils booleanPrettify method * [AAE-10668] Fixed import statements * [AAE-10668] fixed lint problems Co-authored-by: Diogo Bastos --- .../about-license-list.component.html | 2 +- .../about-license-list.component.scss | 4 + .../about-license-list.component.ts | 1 + lib/core/src/lib/about/about.component.ts | 19 ++- lib/core/src/lib/utils/object-utils.spec.ts | 116 ++++++++++++++++++ lib/core/src/lib/utils/object-utils.ts | 57 +++++++++ lib/core/src/lib/utils/public-api.ts | 1 + lib/core/src/lib/utils/string-utils.spec.ts | 75 +++++++++++ lib/core/src/lib/utils/string-utils.ts | 56 +++++++++ 9 files changed, 326 insertions(+), 5 deletions(-) create mode 100644 lib/core/src/lib/about/about-license-list/about-license-list.component.scss create mode 100644 lib/core/src/lib/utils/string-utils.spec.ts create mode 100644 lib/core/src/lib/utils/string-utils.ts diff --git a/lib/core/src/lib/about/about-license-list/about-license-list.component.html b/lib/core/src/lib/about/about-license-list/about-license-list.component.html index f58a2cf6fa..d84eb685e5 100644 --- a/lib/core/src/lib/about/about-license-list/about-license-list.component.html +++ b/lib/core/src/lib/about/about-license-list/about-license-list.component.html @@ -3,7 +3,7 @@ {{ column.header | translate }} - {{ column.cell(row) }} + diff --git a/lib/core/src/lib/about/about-license-list/about-license-list.component.scss b/lib/core/src/lib/about/about-license-list/about-license-list.component.scss new file mode 100644 index 0000000000..ed2bfbb093 --- /dev/null +++ b/lib/core/src/lib/about/about-license-list/about-license-list.component.scss @@ -0,0 +1,4 @@ +mat-cell { + white-space: pre-line; + line-height: 30px; +} diff --git a/lib/core/src/lib/about/about-license-list/about-license-list.component.ts b/lib/core/src/lib/about/about-license-list/about-license-list.component.ts index 30133f2ab9..7c091e2e6a 100644 --- a/lib/core/src/lib/about/about-license-list/about-license-list.component.ts +++ b/lib/core/src/lib/about/about-license-list/about-license-list.component.ts @@ -21,6 +21,7 @@ import { LicenseData } from '../interfaces'; @Component({ selector: 'adf-about-license-list', templateUrl: './about-license-list.component.html', + styleUrls: ['./about-license-list.component.scss'], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush }) diff --git a/lib/core/src/lib/about/about.component.ts b/lib/core/src/lib/about/about.component.ts index 9227764704..f8dfcf5265 100644 --- a/lib/core/src/lib/about/about.component.ts +++ b/lib/core/src/lib/about/about.component.ts @@ -25,6 +25,8 @@ import { BpmProductVersionModel } from '../models/product-version.model'; import { AuthenticationService } from '../services/authentication.service'; import { DiscoveryApiService } from '../services/discovery-api.service'; import { LicenseData, PackageInfo, StatusData } from './interfaces'; +import { ObjectUtils } from '../utils/object-utils'; +import { StringUtils } from '../utils/string-utils'; @Component({ selector: 'adf-about', @@ -101,10 +103,19 @@ export class AboutComponent implements OnInit { })); if (repository.license) { - this.licenseEntries = Object.keys(repository.license).map((key) => ({ - property: key, - value: repository.license[key] - })); + this.licenseEntries = Object.keys(repository.license).map((key) => { + if (ObjectUtils.isObject(repository.license[key])) { + return { + property: key, + value: ObjectUtils.booleanPrettify(repository.license[key], StringUtils.prettifyBooleanEnabled) + }; + }; + + return { + property: key, + value: repository.license[key] + }; + }); } }); } diff --git a/lib/core/src/lib/utils/object-utils.spec.ts b/lib/core/src/lib/utils/object-utils.spec.ts index ddd27d6d6b..076c8121fc 100644 --- a/lib/core/src/lib/utils/object-utils.spec.ts +++ b/lib/core/src/lib/utils/object-utils.spec.ts @@ -130,4 +130,120 @@ describe('ObjectUtils', () => { }); }); }); + + describe('isObject', () => { + it('should return false for null and undefined types', () => { + expect(ObjectUtils.isObject(null)).toBe(false); + expect(ObjectUtils.isObject(undefined)).toBe(false); + }); + + it('should return false for non object types', () => { + const numberTest = 1; + const stringTest = 'test'; + expect(ObjectUtils.isObject(numberTest)).toBe(false); + expect(ObjectUtils.isObject(stringTest)).toBe(false); + }); + + it('should return true for object types', () => { + const obj = { + id: 1 + }; + const date = new Date(); + expect(ObjectUtils.isObject(obj)).toBe(true); + expect(ObjectUtils.isObject(date)).toBe(true); + }); + }); + + describe('isEmpty', () => { + it('should return true for empty objects', () => { + const emptyObj = {}; + expect(ObjectUtils.isEmpty(emptyObj)).toBe(true); + }); + + it('should return false for non empty objects', () => { + const obj = { + id: 1 + }; + const date = new Date(); + expect(ObjectUtils.isEmpty(obj)).toBe(false); + expect(ObjectUtils.isEmpty(date)).toBe(false); + }); + }); + + describe('isBooleanObject', () => { + it('should return true for objects with all bollean values', () => { + const obj = { + testOne: true, + testTwo: false + }; + expect(ObjectUtils.isBooleanObject(obj)).toBe(true); + }); + + it('should return false for objects with at least one non boolean value', () => { + const objOne = { + testOne: true, + testTwo: 1 + }; + const objTwo = { + testOne: 1, + testTwo: 2 + }; + expect(ObjectUtils.isBooleanObject(objOne)).toBe(false); + expect(ObjectUtils.isBooleanObject(objTwo)).toBe(false); + }); + }); + + describe('booleanPrettify', () => { + it('should return empty string for empty types', () => { + expect(ObjectUtils.booleanPrettify(null)).toBe(''); + expect(ObjectUtils.booleanPrettify(undefined)).toBe(''); + }); + + it('should return string if not object', () => { + const numberTest = 1; + expect(ObjectUtils.booleanPrettify(numberTest)).toBe(numberTest.toString()); + }); + + it('should return empty string for empty objects', () => { + const obj = {}; + expect(ObjectUtils.booleanPrettify(obj)).toBe(''); + }); + + it('should return string for objects with no keys', () => { + const date = new Date(); + expect(ObjectUtils.booleanPrettify(date)).toBe(date.toString()); + }); + + it('should return empty string for objects containing non boolean values', () => { + const nonBooleanObjOne = { + testOne: 1, + testTwo: 2 + }; + const nonBooleanObjTwo = { + testOne: 1, + testTwo: false + }; + expect(ObjectUtils.booleanPrettify(nonBooleanObjOne)).toBe(''); + expect(ObjectUtils.booleanPrettify(nonBooleanObjTwo)).toBe(''); + }); + + it('should return string with either ✅ or ❌ symbols if object with boolean values', () => { + const obj = { + testOne: true, + testTwo: false + }; + expect(ObjectUtils.booleanPrettify(obj)).toBe('✅ testOne\n❌ testTwo'); + }); + + it('should return enhanced string with either ✅ or ❌ symbols if object with boolean values', () => { + const obj = { + testOne: true, + testTwo: false + }; + + const enhancer = (e: string) => e + 'test'; + + expect(ObjectUtils.booleanPrettify(obj, enhancer)).toBe('✅ testOnetest\n❌ testTwotest'); + }); + }); }); diff --git a/lib/core/src/lib/utils/object-utils.ts b/lib/core/src/lib/utils/object-utils.ts index 3916434984..1ef1812b00 100644 --- a/lib/core/src/lib/utils/object-utils.ts +++ b/lib/core/src/lib/utils/object-utils.ts @@ -65,4 +65,61 @@ export class ObjectUtils { return result; } + + static isObject(target: any): boolean { + return target === Object(target); + } + + static isEmpty(target: any): boolean { + return target && Object.keys(target).length === 0 && Object.getPrototypeOf(target) === Object.prototype; + } + + static hasKeys(target: any): boolean { + return target && Object.keys(target).length > 0; + } + + static isBooleanObject(target: any): boolean { + return Object.values(target).every(value => typeof value === 'boolean'); + } + + static booleanPrettify(target: any, enhancer?: (param: string) => string): string { + + if ( + !target || + ObjectUtils.isEmpty(target) || + !ObjectUtils.isBooleanObject(target) + ) { + return ''; + } + + if ( + !ObjectUtils.isObject(target) || + !ObjectUtils.hasKeys(target) + ) { + return target.toString(); + } + + const greenBorderWhiteCheckSymbol = '✅'; + const redCrossSymbol = '❌'; + + target = Object.keys(target).map((key) => { + if (target[key]) { + if (enhancer) { + return `${greenBorderWhiteCheckSymbol} ${enhancer(key)}`; + } else { + return `${greenBorderWhiteCheckSymbol} ${key}`; + } + + } + + if (enhancer) { + return `${redCrossSymbol} ${enhancer(key)}`; + } else { + return `${redCrossSymbol} ${key}`; + } + + }).join('\n'); + + return target; + } } diff --git a/lib/core/src/lib/utils/public-api.ts b/lib/core/src/lib/utils/public-api.ts index 6ec1e00e61..ecb16b1204 100644 --- a/lib/core/src/lib/utils/public-api.ts +++ b/lib/core/src/lib/utils/public-api.ts @@ -19,3 +19,4 @@ export * from './object-utils'; export * from './file-utils'; export * from './moment-date-formats.model'; export * from './moment-date-adapter'; +export * from './string-utils'; diff --git a/lib/core/src/lib/utils/string-utils.spec.ts b/lib/core/src/lib/utils/string-utils.spec.ts new file mode 100644 index 0000000000..c9ac4ef2bd --- /dev/null +++ b/lib/core/src/lib/utils/string-utils.spec.ts @@ -0,0 +1,75 @@ +/*! + * @license + * Copyright 2019 Alfresco Software, Ltd. + * + * 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 { StringUtils } from './string-utils'; + +describe('StringUtils', () => { + + describe('capitalize', () => { + it('should uppercase first letter of word and lowercase the rest', () => { + const lowercaseWord = 'test'; + const uppercaseWord = 'TEST'; + const mixedWord = 'tEsT'; + + expect(StringUtils.capitalize(lowercaseWord)).toBe('Test'); + expect(StringUtils.capitalize(uppercaseWord)).toBe('Test'); + expect(StringUtils.capitalize(mixedWord)).toBe('Test'); + }); + + it('should uppercase first letter of first word in sentence and lowercase the rest', () => { + const sentence = 'this is a sentence'; + + expect(StringUtils.capitalize(sentence)).toBe('This is a sentence'); + }); + }); + + describe('replaceAll', () => { + it('should replace all instances provided in the delimiters obj', () => { + const test = 'isClusterEnabled'; + const delimiters = { + is: 'are', + Enabled: 'Disabled' + }; + + expect(StringUtils.replaceAll(test, delimiters)).toBe('areClusterDisabled'); + }); + + it('should return initial string if delimiters is not an oject', () => { + const test = 'isClusterEnabled'; + const delimiters = 'test'; + + expect(StringUtils.replaceAll(test, delimiters)).toBe('isClusterEnabled'); + }); + }); + + describe('removeAll', () => { + it('should remove all instances described in demiliters arguments in string', () => { + const test = 'isClusterEnabled'; + const delimiters = ['is', 'Enabled']; + + expect(StringUtils.removeAll(test, ...delimiters)).toBe('Cluster'); + }); + }); + + describe('prettifyBooleanEnabled', () => { + it('should remove "is" and "enabled" from strings', () => { + const test = 'isClusterEnabled'; + + expect(StringUtils.prettifyBooleanEnabled(test)).toBe('Cluster'); + }); + }); +}); diff --git a/lib/core/src/lib/utils/string-utils.ts b/lib/core/src/lib/utils/string-utils.ts new file mode 100644 index 0000000000..19b687dc89 --- /dev/null +++ b/lib/core/src/lib/utils/string-utils.ts @@ -0,0 +1,56 @@ +/*! + * @license + * Copyright 2019 Alfresco Software, Ltd. + * + * 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 { ObjectUtils } from './object-utils'; + +export class StringUtils { + + static capitalize(target: string): string { + return target.charAt(0).toUpperCase() + target.slice(1).toLowerCase(); + } + + static replaceAll(target: string, delimiters: any): string { + if (!ObjectUtils.isObject(delimiters)) { + return target; + } + + Object.keys(delimiters).forEach((key) => { + target = target.replace(key, delimiters[key]); + }); + + return target; + } + + static removeAll(target: string, ...delimiters: string[]): string { + const delimiterObj = {}; + delimiters.forEach(delimiter => { + delimiterObj[delimiter] = ''; + }); + + return StringUtils.replaceAll(target, delimiterObj); + } + + static prettifyBooleanEnabled(target: string): string { + const redactedTarget = StringUtils.removeAll(target.toLowerCase(), 'is', 'enabled'); + const bagOfWords = redactedTarget.split(' '); + const capitalizedBagOfWords = bagOfWords.map((word) => StringUtils.capitalize(word)); + + + return capitalizedBagOfWords.join(' '); + } + +}