Merge pull request #3353 from vvlladd28/feature/encode-state-params

Improvment encode-state params for dashboard
This commit is contained in:
Igor Kulikov 2020-08-21 17:04:35 +03:00 committed by GitHub
commit e3dcc96913
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 26 additions and 160 deletions

View File

@ -35,13 +35,11 @@
"@ngrx/effects": "^10.0.0",
"@ngrx/store": "^10.0.0",
"@ngrx/store-devtools": "^10.0.0",
"ngx-sharebuttons": "^8.0.1",
"@ngx-translate/core": "^13.0.0",
"@ngx-translate/http-loader": "^6.0.0",
"ace-builds": "^1.4.12",
"angular-gridster2": "^10.1.3",
"angular2-hotkeys": "^2.2.0",
"base64-js": "^1.3.1",
"canvas-gauges": "^2.1.7",
"compass-sass-mixins": "^0.12.7",
"core-js": "^3.6.5",
@ -70,6 +68,7 @@
"ngx-daterangepicker-material": "^3.0.4",
"ngx-flowchart": "git://github.com/thingsboard/ngx-flowchart.git#master",
"ngx-hm-carousel": "^2.0.0-rc.1",
"ngx-sharebuttons": "^8.0.1",
"ngx-translate-messageformat-compiler": "^4.8.0",
"objectpath": "^2.0.0",
"prettier": "^2.0.5",

View File

@ -26,7 +26,6 @@ import {
import { DOCUMENT } from '@angular/common';
import { forkJoin, Observable, ReplaySubject, throwError } from 'rxjs';
import { HttpClient } from '@angular/common/http';
import { objToBase64 } from '@core/utils';
declare const SystemJS;

View File

@ -17,7 +17,6 @@
import _ from 'lodash';
import { Observable, Subject } from 'rxjs';
import { finalize, share } from 'rxjs/operators';
import base64js from 'base64-js';
import { Datasource } from '@app/shared/models/widget.models';
const varsRegex = /\${([^}]*)}/g;
@ -123,7 +122,8 @@ export function isEmpty(obj: any): boolean {
}
export function formatValue(value: any, dec?: number, units?: string, showZeroDecimals?: boolean): string | undefined {
if (isDefinedAndNotNull(value) && isNumeric(value) && (isDefinedAndNotNull(dec) || isDefinedAndNotNull(units) || Number(value).toString() === value)) {
if (isDefinedAndNotNull(value) && isNumeric(value) &&
(isDefinedAndNotNull(dec) || isDefinedAndNotNull(units) || Number(value).toString() === value)) {
let formatted: string | number = Number(value);
if (isDefinedAndNotNull(dec)) {
formatted = formatted.toFixed(dec);
@ -164,30 +164,23 @@ export function deleteNullProperties(obj: any) {
export function objToBase64(obj: any): string {
const json = JSON.stringify(obj);
const encoded = utf8Encode(json);
return base64js.fromByteArray(encoded);
return btoa(encodeURIComponent(json).replace(/%([0-9A-F]{2})/g,
function toSolidBytes(match, p1) {
return String.fromCharCode(Number('0x' + p1));
}));
}
export function objToBase64URI(obj: any): string {
return encodeURIComponent(objToBase64(obj));
}
export function base64toObj(b64Encoded: string): any {
const encoded: Uint8Array | number[] = base64js.toByteArray(b64Encoded);
const json = utf8Decode(encoded);
const json = decodeURIComponent(atob(b64Encoded).split('').map((c) => {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
}).join(''));
return JSON.parse(json);
}
function utf8Encode(str: string): Uint8Array | number[] {
let result: Uint8Array | number[];
if (isUndefined(Uint8Array)) {
result = utf8ToBytes(str);
} else {
result = new Uint8Array(utf8ToBytes(str));
}
return result;
}
function utf8Decode(bytes: Uint8Array | number[]): string {
return utf8Slice(bytes, 0, bytes.length);
}
const scrollRegex = /(auto|scroll)/;
function parentNodes(node: Node, nodes: Node[]): Node[] {
@ -275,129 +268,6 @@ function easeInOut(
);
}
function utf8Slice(buf: Uint8Array | number[], start: number, end: number): string {
let res = '';
let tmp = '';
end = Math.min(buf.length, end || Infinity);
start = start || 0;
for (let i = start; i < end; i++) {
if (buf[i] <= 0x7F) {
res += decodeUtf8Char(tmp) + String.fromCharCode(buf[i]);
tmp = '';
} else {
tmp += '%' + buf[i].toString(16);
}
}
return res + decodeUtf8Char(tmp);
}
function decodeUtf8Char(str: string): string {
try {
return decodeURIComponent(str);
} catch (err) {
return String.fromCharCode(0xFFFD); // UTF 8 invalid char
}
}
function utf8ToBytes(input: string, units?: number): number[] {
units = units || Infinity;
let codePoint: number;
const length = input.length;
let leadSurrogate: number = null;
const bytes: number[] = [];
let i = 0;
for (; i < length; i++) {
codePoint = input.charCodeAt(i);
// is surrogate component
if (codePoint > 0xD7FF && codePoint < 0xE000) {
// last char was a lead
if (leadSurrogate) {
// 2 leads in a row
if (codePoint < 0xDC00) {
units -= 3;
if (units > -1) { bytes.push(0xEF, 0xBF, 0xBD); }
leadSurrogate = codePoint;
continue;
} else {
// valid surrogate pair
// tslint:disable-next-line:no-bitwise
codePoint = leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00 | 0x10000;
leadSurrogate = null;
}
} else {
// no lead yet
if (codePoint > 0xDBFF) {
// unexpected trail
units -= 3;
if (units > -1) { bytes.push(0xEF, 0xBF, 0xBD); }
continue;
} else if (i + 1 === length) {
// unpaired lead
units -= 3;
if (units > -1) { bytes.push(0xEF, 0xBF, 0xBD); }
continue;
} else {
// valid lead
leadSurrogate = codePoint;
continue;
}
}
} else if (leadSurrogate) {
// valid bmp char, but last char was a lead
units -= 3;
if (units > -1) { bytes.push(0xEF, 0xBF, 0xBD); }
leadSurrogate = null;
}
// encode utf8
if (codePoint < 0x80) {
units -= 1;
if (units < 0) { break; }
bytes.push(codePoint);
} else if (codePoint < 0x800) {
units -= 2;
if (units < 0) { break; }
bytes.push(
// tslint:disable-next-line:no-bitwise
codePoint >> 0x6 | 0xC0,
// tslint:disable-next-line:no-bitwise
codePoint & 0x3F | 0x80
);
} else if (codePoint < 0x10000) {
units -= 3;
if (units < 0) { break; }
bytes.push(
// tslint:disable-next-line:no-bitwise
codePoint >> 0xC | 0xE0,
// tslint:disable-next-line:no-bitwise
codePoint >> 0x6 & 0x3F | 0x80,
// tslint:disable-next-line:no-bitwise
codePoint & 0x3F | 0x80
);
} else if (codePoint < 0x200000) {
units -= 4;
if (units < 0) { break; }
bytes.push(
// tslint:disable-next-line:no-bitwise
codePoint >> 0x12 | 0xF0,
// tslint:disable-next-line:no-bitwise
codePoint >> 0xC & 0x3F | 0x80,
// tslint:disable-next-line:no-bitwise
codePoint >> 0x6 & 0x3F | 0x80,
// tslint:disable-next-line:no-bitwise
codePoint & 0x3F | 0x80
);
} else {
throw new Error('Invalid code point');
}
}
return bytes;
}
export function deepClone<T>(target: T, ignoreFields?: string[]): T {
if (target === null) {
return target;

View File

@ -24,7 +24,7 @@ import { Router } from '@angular/router';
import { DialogComponent } from '@app/shared/components/dialog.component';
import { UtilsService } from '@core/services/utils.service';
import { Dashboard, DashboardLayoutId } from '@app/shared/models/dashboard.models';
import { objToBase64 } from '@core/utils';
import { objToBase64URI } from '@core/utils';
import { DashboardUtilsService } from '@core/services/dashboard-utils.service';
import { EntityId } from '@app/shared/models/id/entity-id';
import { Widget } from '@app/shared/models/widget.models';
@ -205,7 +205,7 @@ export class AddWidgetToDashboardDialogComponent extends
id: targetState,
params: {}
};
const state = objToBase64([ stateObject ]);
const state = objToBase64URI([ stateObject ]);
url = `/dashboards/${theDashboard.id.id}?state=${state}`;
} else {
url = `/dashboards/${theDashboard.id.id}`;

View File

@ -35,7 +35,6 @@ import {
} from '@angular/core';
import { DashboardWidget } from '@home/models/dashboard-component.models';
import {
Datasource,
defaultLegendConfig,
LegendConfig,
LegendData,
@ -55,7 +54,7 @@ import { AppState } from '@core/core.state';
import { WidgetService } from '@core/http/widget.service';
import { UtilsService } from '@core/services/utils.service';
import { forkJoin, Observable, of, ReplaySubject, Subscription, throwError } from 'rxjs';
import { deepClone, isDefined, objToBase64 } from '@core/utils';
import { deepClone, isDefined, objToBase64URI } from '@core/utils';
import {
IDynamicWidgetComponent,
WidgetContext,
@ -68,7 +67,8 @@ import {
StateObject,
StateParams,
SubscriptionEntityInfo,
SubscriptionInfo, SubscriptionMessage,
SubscriptionInfo,
SubscriptionMessage,
WidgetSubscriptionContext,
WidgetSubscriptionOptions
} from '@core/api/widget-api.models';
@ -80,11 +80,9 @@ import { catchError, switchMap } from 'rxjs/operators';
import { ActionNotificationShow } from '@core/notification/notification.actions';
import { TimeService } from '@core/services/time.service';
import { DeviceService } from '@app/core/http/device.service';
import { AlarmService } from '@app/core/http/alarm.service';
import { ExceptionData } from '@shared/models/error.models';
import { WidgetComponentService } from './widget-component.service';
import { Timewindow } from '@shared/models/time/time.models';
import { AlarmSearchStatus } from '@shared/models/alarm.models';
import { CancelAnimationFrame, RafService } from '@core/services/raf.service';
import { DashboardService } from '@core/http/dashboard.service';
import { WidgetSubscription } from '@core/api/widget-subscription';
@ -688,7 +686,7 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI
private destroyDynamicWidgetComponent() {
if (this.widgetContext.$containerParent && this.widgetResize$) {
this.widgetResize$.disconnect()
this.widgetResize$.disconnect();
}
if (this.dynamicWidgetComponentRef) {
this.dynamicWidgetComponentRef.destroy();
@ -1023,7 +1021,7 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI
if (targetDashboardStateId) {
stateObject.id = targetDashboardStateId;
}
const state = objToBase64([ stateObject ]);
const state = objToBase64URI([ stateObject ]);
const isSinglePage = this.route.snapshot.data.singlePageMode;
let url;
if (isSinglePage) {

View File

@ -23,7 +23,7 @@ import { StateControllerComponent } from './state-controller.component';
import { StatesControllerService } from '@home/pages/dashboard/states/states-controller.service';
import { EntityId } from '@app/shared/models/id/entity-id';
import { UtilsService } from '@core/services/utils.service';
import { base64toObj, objToBase64 } from '@app/core/utils';
import { base64toObj, objToBase64URI } from '@app/core/utils';
import { DashboardUtilsService } from '@core/services/dashboard-utils.service';
import { EntityService } from '@core/http/entity.service';
@ -237,7 +237,7 @@ export class DefaultStateControllerComponent extends StateControllerComponent im
private updateLocation() {
if (this.stateObject[0].id) {
const newState = objToBase64(this.stateObject);
const newState = objToBase64URI(this.stateObject);
this.updateStateParam(newState);
}
}

View File

@ -23,7 +23,7 @@ import { StateControllerComponent } from './state-controller.component';
import { StatesControllerService } from '@home/pages/dashboard/states/states-controller.service';
import { EntityId } from '@app/shared/models/id/entity-id';
import { UtilsService } from '@core/services/utils.service';
import { base64toObj, insertVariable, isEmpty, objToBase64 } from '@app/core/utils';
import { base64toObj, insertVariable, isEmpty, objToBase64URI } from '@app/core/utils';
import { DashboardUtilsService } from '@core/services/dashboard-utils.service';
import { EntityService } from '@core/http/entity.service';
import { EntityType } from '@shared/models/entity-type.models';
@ -281,7 +281,7 @@ export class EntityStateControllerComponent extends StateControllerComponent imp
if (this.isDefaultState()) {
newState = null;
} else {
newState = objToBase64(this.stateObject);
newState = objToBase64URI(this.stateObject);
}
this.updateStateParam(newState);
}

View File

@ -2138,7 +2138,7 @@ base64-arraybuffer@0.1.5:
resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz#73926771923b5a19747ad666aa5cd4bf9c6e9ce8"
integrity sha1-c5JncZI7Whl0etZmqlzUv5xunOg=
base64-js@^1.0.2, base64-js@^1.3.1:
base64-js@^1.0.2:
version "1.3.1"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1"
integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==