mirror of
https://github.com/OneUptime/oneuptime
synced 2024-11-21 22:59:07 +00:00
Merge branch 'telemetry'
This commit is contained in:
commit
a5f2f4e338
2
.gitignore
vendored
2
.gitignore
vendored
@ -97,3 +97,5 @@ Llama/Models/tokenizer*
|
||||
Llama/Models/llama*
|
||||
|
||||
Llama/__pycache__/*
|
||||
|
||||
Examples/otel-dotnet/obj
|
@ -6,12 +6,12 @@ import Name from 'Common/Types/Name';
|
||||
import { DASHBOARD_URL } from 'CommonUI/src/Config';
|
||||
import { JSONObject } from 'Common/Types/JSON';
|
||||
import User from 'Model/Models/User';
|
||||
import JSONFunctions from 'Common/Types/JSONFunctions';
|
||||
import Analytics from 'CommonUI/src/Utils/Analytics';
|
||||
import BaseModel from 'Common/Models/BaseModel';
|
||||
|
||||
export default abstract class LoginUtil {
|
||||
public static login(value: JSONObject): void {
|
||||
const user: User = JSONFunctions.fromJSON(
|
||||
const user: User = BaseModel.fromJSON(
|
||||
value['user'] as JSONObject,
|
||||
User
|
||||
) as User;
|
||||
|
@ -8,19 +8,23 @@ import { TableAccessControl } from '../Types/BaseDatabase/AccessControl';
|
||||
import EnableWorkflowOn from '../Types/BaseDatabase/EnableWorkflowOn';
|
||||
import ObjectID from '../Types/ObjectID';
|
||||
import CommonModel from './CommonModel';
|
||||
import Route from '../Types/API/Route';
|
||||
import { EnableRealtimeEventsOn } from '../Utils/Realtime';
|
||||
|
||||
export default class AnalyticsDataModel extends CommonModel {
|
||||
export default class AnalyticsBaseModel extends CommonModel {
|
||||
public constructor(data: {
|
||||
tableName: string;
|
||||
singularName: string;
|
||||
pluralName: string;
|
||||
tableEngine?: AnalyticsTableEngine | undefined;
|
||||
tableColumns: Array<AnalyticsTableColumn>;
|
||||
crudApiPath: Route;
|
||||
allowAccessIfSubscriptionIsUnpaid?: boolean | undefined;
|
||||
tableBillingAccessControl?: TableBillingAccessControl | undefined;
|
||||
accessControl?: TableAccessControl | undefined;
|
||||
primaryKeys: Array<string>; // this should be the subset of tableColumns
|
||||
enableWorkflowOn?: EnableWorkflowOn | undefined;
|
||||
enableRealtimeEventsOn?: EnableRealtimeEventsOn | undefined;
|
||||
}) {
|
||||
super({
|
||||
tableColumns: data.tableColumns,
|
||||
@ -100,6 +104,8 @@ export default class AnalyticsDataModel extends CommonModel {
|
||||
data.allowAccessIfSubscriptionIsUnpaid || false;
|
||||
this.accessControl = data.accessControl;
|
||||
this.enableWorkflowOn = data.enableWorkflowOn;
|
||||
this.crudApiPath = data.crudApiPath;
|
||||
this.enableRealtimeEventsOn = data.enableRealtimeEventsOn;
|
||||
|
||||
// initialize Arrays.
|
||||
for (const column of this.tableColumns) {
|
||||
@ -141,6 +147,14 @@ export default class AnalyticsDataModel extends CommonModel {
|
||||
this._tableEngine = v;
|
||||
}
|
||||
|
||||
private _enableRealtimeEventsOn: EnableRealtimeEventsOn | undefined;
|
||||
public get enableRealtimeEventsOn(): EnableRealtimeEventsOn | undefined {
|
||||
return this._enableRealtimeEventsOn;
|
||||
}
|
||||
public set enableRealtimeEventsOn(v: EnableRealtimeEventsOn | undefined) {
|
||||
this._enableRealtimeEventsOn = v;
|
||||
}
|
||||
|
||||
private _primaryKeys: Array<string> = [];
|
||||
public get primaryKeys(): Array<string> {
|
||||
return this._primaryKeys;
|
||||
@ -185,6 +199,14 @@ export default class AnalyticsDataModel extends CommonModel {
|
||||
this._allowAccessIfSubscriptionIsUnpaid = v;
|
||||
}
|
||||
|
||||
private _crudApiPath!: Route;
|
||||
public get crudApiPath(): Route {
|
||||
return this._crudApiPath;
|
||||
}
|
||||
public set crudApiPath(v: Route) {
|
||||
this._crudApiPath = v;
|
||||
}
|
||||
|
||||
public getTenantColumn(): AnalyticsTableColumn | null {
|
||||
const column: AnalyticsTableColumn | undefined = this.tableColumns.find(
|
||||
(column: AnalyticsTableColumn) => {
|
||||
@ -199,6 +221,16 @@ export default class AnalyticsDataModel extends CommonModel {
|
||||
return column;
|
||||
}
|
||||
|
||||
public getTenantColumnValue(): ObjectID | null {
|
||||
const column: AnalyticsTableColumn | null = this.getTenantColumn();
|
||||
|
||||
if (!column) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.getColumnValue(column.key) as ObjectID | null;
|
||||
}
|
||||
|
||||
public getRequiredColumns(): Array<AnalyticsTableColumn> {
|
||||
return this.tableColumns.filter((column: AnalyticsTableColumn) => {
|
||||
return column.required;
|
||||
|
@ -4,7 +4,8 @@ import AnalyticsTableColumn from '../Types/AnalyticsDatabase/TableColumn';
|
||||
import TableColumnType from '../Types/AnalyticsDatabase/TableColumnType';
|
||||
import OneUptimeDate from '../Types/Date';
|
||||
import BadDataException from '../Types/Exception/BadDataException';
|
||||
import { JSONObject, JSONValue } from '../Types/JSON';
|
||||
import { JSONArray, JSONObject, JSONValue } from '../Types/JSON';
|
||||
import JSONFunctions from '../Types/JSONFunctions';
|
||||
import ObjectID from '../Types/ObjectID';
|
||||
|
||||
export type RecordValue =
|
||||
@ -111,6 +112,39 @@ export default class CommonModel {
|
||||
return this.tableColumns;
|
||||
}
|
||||
|
||||
public static fromJSON<T extends CommonModel>(
|
||||
json: JSONObject | JSONArray | CommonModel | Array<CommonModel>,
|
||||
type: { new (): T }
|
||||
): T | Array<T> {
|
||||
if (Array.isArray(json)) {
|
||||
const arr: Array<T> = [];
|
||||
|
||||
for (const item of json) {
|
||||
if (item instanceof CommonModel) {
|
||||
arr.push(item as T);
|
||||
continue;
|
||||
}
|
||||
|
||||
arr.push(new type().fromJSON(item) as T);
|
||||
}
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
if (json instanceof CommonModel) {
|
||||
return json as T;
|
||||
}
|
||||
|
||||
return new type().fromJSON(json) as T;
|
||||
}
|
||||
|
||||
public static toJSON<T extends CommonModel>(
|
||||
model: T,
|
||||
_modelType: { new (): T }
|
||||
): JSONObject {
|
||||
return model.toJSON();
|
||||
}
|
||||
|
||||
public fromJSON(json: JSONObject): CommonModel {
|
||||
for (const key in json) {
|
||||
this.setColumnValue(key, json[key]);
|
||||
@ -133,12 +167,10 @@ export default class CommonModel {
|
||||
}
|
||||
|
||||
if (recordValue instanceof Array) {
|
||||
if (
|
||||
recordValue.length > 0 &&
|
||||
recordValue[0] instanceof CommonModel
|
||||
) {
|
||||
if (recordValue.length > 0 && column.nestedModelType) {
|
||||
json[column.key] = CommonModel.toJSONArray(
|
||||
recordValue as Array<CommonModel>
|
||||
recordValue as Array<CommonModel>,
|
||||
column.nestedModelType
|
||||
);
|
||||
}
|
||||
|
||||
@ -148,16 +180,21 @@ export default class CommonModel {
|
||||
json[column.key] = recordValue;
|
||||
});
|
||||
|
||||
return json;
|
||||
return JSONFunctions.serialize(json);
|
||||
}
|
||||
|
||||
public static fromJSONArray<TBaseModel extends CommonModel>(
|
||||
modelType: { new (): CommonModel },
|
||||
jsonArray: Array<JSONObject>
|
||||
jsonArray: Array<JSONObject | CommonModel>,
|
||||
modelType: { new (): CommonModel }
|
||||
): Array<TBaseModel> {
|
||||
const models: Array<CommonModel> = [];
|
||||
|
||||
jsonArray.forEach((json: JSONObject) => {
|
||||
jsonArray.forEach((json: JSONObject | CommonModel) => {
|
||||
if (json instanceof CommonModel) {
|
||||
models.push(json);
|
||||
return;
|
||||
}
|
||||
|
||||
const model: CommonModel = new modelType();
|
||||
model.fromJSON(json);
|
||||
models.push(model);
|
||||
@ -166,11 +203,14 @@ export default class CommonModel {
|
||||
return models as Array<TBaseModel>;
|
||||
}
|
||||
|
||||
public static toJSONArray(models: Array<CommonModel>): Array<JSONObject> {
|
||||
public static toJSONArray(
|
||||
models: Array<CommonModel>,
|
||||
modelType: { new (): CommonModel }
|
||||
): Array<JSONObject> {
|
||||
const json: Array<JSONObject> = [];
|
||||
|
||||
models.forEach((model: CommonModel) => {
|
||||
json.push(model.toJSON());
|
||||
json.push(this.toJSON(model, modelType));
|
||||
});
|
||||
|
||||
return json;
|
||||
|
@ -37,6 +37,7 @@ import IconProp from '../Types/Icon/IconProp';
|
||||
import Text from '../Types/Text';
|
||||
import { getColumnBillingAccessControlForAllColumns } from '../Types/Database/AccessControl/ColumnBillingAccessControl';
|
||||
import ColumnBillingAccessControl from '../Types/BaseDatabase/ColumnBillingAccessControl';
|
||||
import JSONFunctions from '../Types/JSONFunctions';
|
||||
|
||||
export type DbTypes =
|
||||
| string
|
||||
@ -530,4 +531,180 @@ export default class BaseModel extends BaseEntity {
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public static toJSON(
|
||||
model: BaseModel,
|
||||
modelType: { new (): BaseModel }
|
||||
): JSONObject {
|
||||
const json: JSONObject = this.toJSONObject(model, modelType);
|
||||
return JSONFunctions.serialize(json);
|
||||
}
|
||||
|
||||
public static toJSONObject(
|
||||
model: BaseModel,
|
||||
modelType: { new (): BaseModel }
|
||||
): JSONObject {
|
||||
const json: JSONObject = {};
|
||||
|
||||
const vanillaModel: BaseModel = new modelType();
|
||||
|
||||
for (const key of vanillaModel.getTableColumns().columns) {
|
||||
if ((model as any)[key] === undefined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const tableColumnMetadata: TableColumnMetadata =
|
||||
vanillaModel.getTableColumnMetadata(key);
|
||||
|
||||
if (tableColumnMetadata) {
|
||||
if (
|
||||
(model as any)[key] &&
|
||||
tableColumnMetadata.modelType &&
|
||||
tableColumnMetadata.type === TableColumnType.Entity &&
|
||||
(model as any)[key] instanceof BaseModel
|
||||
) {
|
||||
(json as any)[key] = this.toJSONObject(
|
||||
(model as any)[key],
|
||||
tableColumnMetadata.modelType
|
||||
);
|
||||
} else if (
|
||||
(model as any)[key] &&
|
||||
Array.isArray((model as any)[key]) &&
|
||||
(model as any)[key].length > 0 &&
|
||||
tableColumnMetadata.modelType &&
|
||||
tableColumnMetadata.type === TableColumnType.EntityArray
|
||||
) {
|
||||
(json as any)[key] = this.toJSONObjectArray(
|
||||
(model as any)[key] as Array<BaseModel>,
|
||||
tableColumnMetadata.modelType
|
||||
);
|
||||
} else {
|
||||
(json as any)[key] = (model as any)[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
public static toJSONObjectArray(
|
||||
list: Array<BaseModel>,
|
||||
modelType: { new (): BaseModel }
|
||||
): JSONArray {
|
||||
const array: JSONArray = [];
|
||||
|
||||
for (const item of list) {
|
||||
array.push(this.toJSONObject(item, modelType));
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
public static toJSONArray(
|
||||
list: Array<BaseModel>,
|
||||
modelType: { new (): BaseModel }
|
||||
): JSONArray {
|
||||
const array: JSONArray = [];
|
||||
|
||||
for (const item of list) {
|
||||
array.push(this.toJSON(item, modelType));
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
private static _fromJSON<T extends BaseModel>(
|
||||
json: JSONObject | T,
|
||||
type: { new (): T }
|
||||
): T {
|
||||
if (json instanceof BaseModel) {
|
||||
return json;
|
||||
}
|
||||
|
||||
json = JSONFunctions.deserialize(json);
|
||||
const baseModel: T = new type();
|
||||
|
||||
for (const key of Object.keys(json)) {
|
||||
const tableColumnMetadata: TableColumnMetadata =
|
||||
baseModel.getTableColumnMetadata(key);
|
||||
if (tableColumnMetadata) {
|
||||
if (
|
||||
json[key] &&
|
||||
tableColumnMetadata.modelType &&
|
||||
tableColumnMetadata.type === TableColumnType.Entity
|
||||
) {
|
||||
if (
|
||||
json[key] &&
|
||||
Array.isArray(json[key]) &&
|
||||
(json[key] as Array<any>).length > 0
|
||||
) {
|
||||
json[key] = (json[key] as Array<any>)[0];
|
||||
}
|
||||
|
||||
(baseModel as any)[key] = this.fromJSON(
|
||||
json[key] as JSONObject,
|
||||
tableColumnMetadata.modelType
|
||||
);
|
||||
} else if (
|
||||
json[key] &&
|
||||
tableColumnMetadata.modelType &&
|
||||
tableColumnMetadata.type === TableColumnType.EntityArray
|
||||
) {
|
||||
if (json[key] && !Array.isArray(json[key])) {
|
||||
json[key] = [json[key]];
|
||||
}
|
||||
|
||||
(baseModel as any)[key] = this.fromJSONArray(
|
||||
json[key] as JSONArray,
|
||||
tableColumnMetadata.modelType
|
||||
);
|
||||
} else {
|
||||
(baseModel as any)[key] = json[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return baseModel as T;
|
||||
}
|
||||
|
||||
public static fromJSON<T extends BaseModel>(
|
||||
json: JSONObject | JSONArray,
|
||||
type: { new (): T }
|
||||
): T | Array<T> {
|
||||
if (Array.isArray(json)) {
|
||||
const arr: Array<T> = [];
|
||||
|
||||
for (const item of json) {
|
||||
arr.push(this._fromJSON<T>(item, type));
|
||||
}
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
return this._fromJSON<T>(json, type);
|
||||
}
|
||||
|
||||
public static fromJSONObject<T extends BaseModel>(
|
||||
json: JSONObject | T,
|
||||
type: { new (): T }
|
||||
): T {
|
||||
if (json instanceof BaseModel) {
|
||||
return json;
|
||||
}
|
||||
|
||||
return this.fromJSON<T>(json, type) as T;
|
||||
}
|
||||
|
||||
public static fromJSONArray<T extends BaseModel>(
|
||||
json: Array<JSONObject | T>,
|
||||
type: { new (): T }
|
||||
): Array<T> {
|
||||
const arr: Array<T> = [];
|
||||
|
||||
for (const item of json) {
|
||||
arr.push(this._fromJSON<T>(item, type));
|
||||
}
|
||||
|
||||
return arr;
|
||||
}
|
||||
}
|
||||
|
@ -27,3 +27,5 @@ export const ApiReferenceRoute: Route = new Route('/reference');
|
||||
export const AdminDashboardRoute: Route = new Route('/admin');
|
||||
|
||||
export const IngestorRoute: Route = new Route('/ingestor');
|
||||
|
||||
export const RealtimeRoute: Route = new Route('/realtime/socket');
|
||||
|
@ -28,12 +28,12 @@ describe('JSONFunctions Class', () => {
|
||||
|
||||
describe('toJSON and fromJSON Methods', () => {
|
||||
test('toJSON returns a valid JSON object', () => {
|
||||
const json: JSONObject = JSONFunctions.toJSON(baseModel, BaseModel);
|
||||
const json: JSONObject = BaseModel.toJSON(baseModel, BaseModel);
|
||||
expect(json).toEqual(expect.objectContaining({}));
|
||||
});
|
||||
|
||||
test('toJSONObject returns a valid JSON object', () => {
|
||||
const json: JSONObject = JSONFunctions.toJSONObject(
|
||||
const json: JSONObject = BaseModel.toJSONObject(
|
||||
baseModel,
|
||||
BaseModel
|
||||
);
|
||||
@ -42,7 +42,7 @@ describe('JSONFunctions Class', () => {
|
||||
|
||||
test('fromJSON returns a BaseModel instance', () => {
|
||||
const json: JSONObject = { name: 'oneuptime' };
|
||||
const result: BaseModel | BaseModel[] = JSONFunctions.fromJSON(
|
||||
const result: BaseModel | BaseModel[] = BaseModel.fromJSON(
|
||||
json,
|
||||
BaseModel
|
||||
);
|
||||
|
@ -1,11 +1,17 @@
|
||||
import BaseModel from '../../Models/BaseModel';
|
||||
import AnalyticsBaseModel from '../../AnalyticsModels/BaseModel';
|
||||
import { JSONArray, JSONObject, JSONObjectOrArray } from '../JSON';
|
||||
import JSONFunctions from '../JSONFunctions';
|
||||
import Typeof from '../Typeof';
|
||||
import Headers from './Headers';
|
||||
|
||||
export default class HTTPResponse<
|
||||
T extends JSONObjectOrArray | BaseModel | Array<BaseModel>
|
||||
T extends
|
||||
| JSONObjectOrArray
|
||||
| BaseModel
|
||||
| Array<BaseModel>
|
||||
| AnalyticsBaseModel
|
||||
| Array<AnalyticsBaseModel>
|
||||
> {
|
||||
private _statusCode: number = -1;
|
||||
public get statusCode(): number {
|
||||
|
@ -112,9 +112,17 @@ export default class AnalyticsTableColumn {
|
||||
this._nestedModel = v;
|
||||
}
|
||||
|
||||
private _nestedModelType?: { new (): NestedModel } | undefined;
|
||||
public get nestedModelType(): { new (): NestedModel } | undefined {
|
||||
return this._nestedModelType;
|
||||
}
|
||||
public set nestedModelType(v: { new (): NestedModel } | undefined) {
|
||||
this._nestedModelType = v;
|
||||
}
|
||||
|
||||
public constructor(data: {
|
||||
key: string;
|
||||
nestedModel?: NestedModel | undefined;
|
||||
nestedModelType?: { new (): NestedModel } | undefined;
|
||||
title: string;
|
||||
description: string;
|
||||
required: boolean;
|
||||
@ -128,7 +136,10 @@ export default class AnalyticsTableColumn {
|
||||
| (() => Date | string | number | boolean)
|
||||
| undefined;
|
||||
}) {
|
||||
if (data.type === TableColumnType.NestedModel && !data.nestedModel) {
|
||||
if (
|
||||
data.type === TableColumnType.NestedModel &&
|
||||
!data.nestedModelType
|
||||
) {
|
||||
throw new Error('NestedModel is required when type is NestedModel');
|
||||
}
|
||||
|
||||
@ -144,6 +155,9 @@ export default class AnalyticsTableColumn {
|
||||
this.billingAccessControl = data.billingAccessControl;
|
||||
this.allowAccessIfSubscriptionIsUnpaid =
|
||||
data.allowAccessIfSubscriptionIsUnpaid || false;
|
||||
this.nestedModel = data.nestedModel;
|
||||
if (data.nestedModelType) {
|
||||
this.nestedModel = new data.nestedModelType();
|
||||
this.nestedModelType = data.nestedModelType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
6
Common/Types/BaseDatabase/DatabaseType.ts
Normal file
6
Common/Types/BaseDatabase/DatabaseType.ts
Normal file
@ -0,0 +1,6 @@
|
||||
enum DatabaseType {
|
||||
Database = 'Database',
|
||||
AnalyticsDatabase = 'AnalyticsDatabase',
|
||||
}
|
||||
|
||||
export default DatabaseType;
|
@ -4,6 +4,7 @@ enum CodeType {
|
||||
HTML = 'html',
|
||||
JSON = 'json',
|
||||
Markdown = 'markdown',
|
||||
SQL = 'sql',
|
||||
// TODO add more mime types.
|
||||
}
|
||||
|
||||
|
@ -10,13 +10,16 @@ enum IconProp {
|
||||
Settings = 'Settings',
|
||||
Criteria = 'Criteria',
|
||||
Notification = 'Notification',
|
||||
CursorArrowRays = 'CursorArrowRays',
|
||||
Cube = 'Cube',
|
||||
Squares = 'Squares',
|
||||
RectangleStack = 'RectangleStack',
|
||||
ChartBar = 'ChartBar',
|
||||
SquareStack = 'SquareStack',
|
||||
Help = 'Help',
|
||||
JSON = 'JSON',
|
||||
Signal = 'Signal',
|
||||
Stop = 'Stop',
|
||||
Database = 'Database',
|
||||
ChevronDown = 'ChevronDown',
|
||||
Pencil = 'Pencil',
|
||||
|
@ -3,8 +3,6 @@ import DatabaseProperty from './Database/DatabaseProperty';
|
||||
import OneUptimeDate from './Date';
|
||||
import BaseModel from '../Models/BaseModel';
|
||||
import { JSONArray, JSONObject, JSONValue, ObjectType } from './JSON';
|
||||
import { TableColumnMetadata } from '../Types/Database/TableColumn';
|
||||
import TableColumnType from './Database/TableColumnType';
|
||||
import SerializableObject from './SerializableObject';
|
||||
import SerializableObjectDictionary from './SerializableObjectDictionary';
|
||||
import JSON5 from 'json5';
|
||||
@ -20,182 +18,6 @@ export default class JSONFunctions {
|
||||
return Object.keys(obj).length === 0;
|
||||
}
|
||||
|
||||
public static toJSON(
|
||||
model: BaseModel,
|
||||
modelType: { new (): BaseModel }
|
||||
): JSONObject {
|
||||
const json: JSONObject = this.toJSONObject(model, modelType);
|
||||
return JSONFunctions.serialize(json);
|
||||
}
|
||||
|
||||
public static toJSONObject(
|
||||
model: BaseModel,
|
||||
modelType: { new (): BaseModel }
|
||||
): JSONObject {
|
||||
const json: JSONObject = {};
|
||||
|
||||
const vanillaModel: BaseModel = new modelType();
|
||||
|
||||
for (const key of vanillaModel.getTableColumns().columns) {
|
||||
if ((model as any)[key] === undefined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const tableColumnMetadata: TableColumnMetadata =
|
||||
vanillaModel.getTableColumnMetadata(key);
|
||||
|
||||
if (tableColumnMetadata) {
|
||||
if (
|
||||
(model as any)[key] &&
|
||||
tableColumnMetadata.modelType &&
|
||||
tableColumnMetadata.type === TableColumnType.Entity &&
|
||||
(model as any)[key] instanceof BaseModel
|
||||
) {
|
||||
(json as any)[key] = this.toJSONObject(
|
||||
(model as any)[key],
|
||||
tableColumnMetadata.modelType
|
||||
);
|
||||
} else if (
|
||||
(model as any)[key] &&
|
||||
Array.isArray((model as any)[key]) &&
|
||||
(model as any)[key].length > 0 &&
|
||||
tableColumnMetadata.modelType &&
|
||||
tableColumnMetadata.type === TableColumnType.EntityArray
|
||||
) {
|
||||
(json as any)[key] = this.toJSONObjectArray(
|
||||
(model as any)[key] as Array<BaseModel>,
|
||||
tableColumnMetadata.modelType
|
||||
);
|
||||
} else {
|
||||
(json as any)[key] = (model as any)[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
public static toJSONObjectArray(
|
||||
list: Array<BaseModel>,
|
||||
modelType: { new (): BaseModel }
|
||||
): JSONArray {
|
||||
const array: JSONArray = [];
|
||||
|
||||
for (const item of list) {
|
||||
array.push(this.toJSONObject(item, modelType));
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
public static toJSONArray(
|
||||
list: Array<BaseModel>,
|
||||
modelType: { new (): BaseModel }
|
||||
): JSONArray {
|
||||
const array: JSONArray = [];
|
||||
|
||||
for (const item of list) {
|
||||
array.push(this.toJSON(item, modelType));
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
private static _fromJSON<T extends BaseModel>(
|
||||
json: JSONObject | T,
|
||||
type: { new (): T }
|
||||
): T {
|
||||
if (json instanceof BaseModel) {
|
||||
return json;
|
||||
}
|
||||
|
||||
json = JSONFunctions.deserialize(json);
|
||||
const baseModel: T = new type();
|
||||
|
||||
for (const key of Object.keys(json)) {
|
||||
const tableColumnMetadata: TableColumnMetadata =
|
||||
baseModel.getTableColumnMetadata(key);
|
||||
if (tableColumnMetadata) {
|
||||
if (
|
||||
json[key] &&
|
||||
tableColumnMetadata.modelType &&
|
||||
tableColumnMetadata.type === TableColumnType.Entity
|
||||
) {
|
||||
if (
|
||||
json[key] &&
|
||||
Array.isArray(json[key]) &&
|
||||
(json[key] as Array<any>).length > 0
|
||||
) {
|
||||
json[key] = (json[key] as Array<any>)[0];
|
||||
}
|
||||
|
||||
(baseModel as any)[key] = this.fromJSON(
|
||||
json[key] as JSONObject,
|
||||
tableColumnMetadata.modelType
|
||||
);
|
||||
} else if (
|
||||
json[key] &&
|
||||
tableColumnMetadata.modelType &&
|
||||
tableColumnMetadata.type === TableColumnType.EntityArray
|
||||
) {
|
||||
if (json[key] && !Array.isArray(json[key])) {
|
||||
json[key] = [json[key]];
|
||||
}
|
||||
|
||||
(baseModel as any)[key] = this.fromJSONArray(
|
||||
json[key] as JSONArray,
|
||||
tableColumnMetadata.modelType
|
||||
);
|
||||
} else {
|
||||
(baseModel as any)[key] = json[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return baseModel as T;
|
||||
}
|
||||
|
||||
public static fromJSON<T extends BaseModel>(
|
||||
json: JSONObject | JSONArray,
|
||||
type: { new (): T }
|
||||
): T | Array<T> {
|
||||
if (Array.isArray(json)) {
|
||||
const arr: Array<T> = [];
|
||||
|
||||
for (const item of json) {
|
||||
arr.push(this._fromJSON<T>(item, type));
|
||||
}
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
return this._fromJSON<T>(json, type);
|
||||
}
|
||||
|
||||
public static fromJSONObject<T extends BaseModel>(
|
||||
json: JSONObject | T,
|
||||
type: { new (): T }
|
||||
): T {
|
||||
if (json instanceof BaseModel) {
|
||||
return json;
|
||||
}
|
||||
|
||||
return this.fromJSON<T>(json, type) as T;
|
||||
}
|
||||
|
||||
public static fromJSONArray<T extends BaseModel>(
|
||||
json: Array<JSONObject | T>,
|
||||
type: { new (): T }
|
||||
): Array<T> {
|
||||
const arr: Array<T> = [];
|
||||
|
||||
for (const item of json) {
|
||||
arr.push(this._fromJSON<T>(item, type));
|
||||
}
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
public static toCompressedString(val: JSONValue): string {
|
||||
return JSON.stringify(val, null, 2);
|
||||
}
|
||||
@ -262,7 +84,7 @@ export default class JSONFunctions {
|
||||
) {
|
||||
return val;
|
||||
} else if (val instanceof BaseModel) {
|
||||
return this.toJSON(val, BaseModel);
|
||||
return BaseModel.toJSON(val, BaseModel);
|
||||
} else if (typeof val === Typeof.Number) {
|
||||
return val;
|
||||
} else if (ArrayBuffer.isView(val)) {
|
||||
|
@ -42,6 +42,13 @@ enum Permission {
|
||||
CanEditProjectApiKey = 'CanEditProjectApiKey',
|
||||
CanEditProjectApiKeyPermissions = 'CanEditProjectApiKeyPermissions',
|
||||
|
||||
// Logs
|
||||
|
||||
CanCreateTelemetryServiceLog = 'CanCreateTelemetryServiceLog',
|
||||
CanDeleteTelemetryServiceLog = 'CanDeleteTelemetryServiceLog',
|
||||
CanEditTelemetryServiceLog = 'CanEditTelemetryServiceLog',
|
||||
CanReadTelemetryServiceLog = 'CanReadTelemetryServiceLog',
|
||||
|
||||
// Billing Permissions (Owner Permission)
|
||||
CanManageProjectBilling = 'CanManageProjectBilling',
|
||||
|
||||
@ -58,10 +65,10 @@ enum Permission {
|
||||
CanEditProjectProbe = 'CanEditProjectProbe',
|
||||
CanReadProjectProbe = 'CanReadProjectProbe',
|
||||
|
||||
CanCreateService = 'CanCreateService',
|
||||
CanDeleteService = 'CanDeleteService',
|
||||
CanEditService = 'CanEditService',
|
||||
CanReadService = 'CanReadService',
|
||||
CanCreateTelemetryService = 'CanCreateTelemetryService',
|
||||
CanDeleteTelemetryService = 'CanDeleteTelemetryService',
|
||||
CanEditTelemetryService = 'CanEditTelemetryService',
|
||||
CanReadTelemetryService = 'CanReadTelemetryService',
|
||||
|
||||
CanCreateMonitorGroupResource = 'CanCreateMonitorGroupResource',
|
||||
CanDeleteMonitorGroupResource = 'CanDeleteMonitorGroupResource',
|
||||
@ -1834,31 +1841,32 @@ export class PermissionHelper {
|
||||
},
|
||||
|
||||
{
|
||||
permission: Permission.CanCreateService,
|
||||
title: 'Can Create Service',
|
||||
description: 'This permission can create Service this project.',
|
||||
isAssignableToTenant: true,
|
||||
isAccessControlPermission: true,
|
||||
},
|
||||
{
|
||||
permission: Permission.CanDeleteService,
|
||||
title: 'Can Delete Service',
|
||||
permission: Permission.CanCreateTelemetryService,
|
||||
title: 'Can Create Telemetry Service',
|
||||
description:
|
||||
'This permission can delete Service of this project.',
|
||||
'This permission can create Telemetry Service this project.',
|
||||
isAssignableToTenant: true,
|
||||
isAccessControlPermission: true,
|
||||
},
|
||||
{
|
||||
permission: Permission.CanEditService,
|
||||
title: 'Can Edit Service',
|
||||
permission: Permission.CanDeleteTelemetryService,
|
||||
title: 'Can Delete Telemetry Service',
|
||||
description:
|
||||
'This permission can edit Service of this project.',
|
||||
'This permission can delete Telemetry Service of this project.',
|
||||
isAssignableToTenant: true,
|
||||
isAccessControlPermission: true,
|
||||
},
|
||||
{
|
||||
permission: Permission.CanReadService,
|
||||
title: 'Can Read Service',
|
||||
permission: Permission.CanEditTelemetryService,
|
||||
title: 'Can Edit Telemetry Service',
|
||||
description:
|
||||
'This permission can edit Telemetry Service of this project.',
|
||||
isAssignableToTenant: true,
|
||||
isAccessControlPermission: true,
|
||||
},
|
||||
{
|
||||
permission: Permission.CanReadTelemetryService,
|
||||
title: 'Can Read Telemetry Service',
|
||||
description:
|
||||
'This permission can read Service of this project.',
|
||||
isAssignableToTenant: true,
|
||||
@ -2112,6 +2120,43 @@ export class PermissionHelper {
|
||||
isAccessControlPermission: false,
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
permission: Permission.CanCreateTelemetryServiceLog,
|
||||
title: 'Can Create Telemetry Service Log',
|
||||
description:
|
||||
'This permission can create Telemetry Service Log this project.',
|
||||
isAssignableToTenant: true,
|
||||
isAccessControlPermission: false,
|
||||
},
|
||||
{
|
||||
permission: Permission.CanDeleteTelemetryServiceLog,
|
||||
title: 'Can Delete Telemetry Service Log',
|
||||
description:
|
||||
'This permission can delete Telemetry Service Log of this project.',
|
||||
isAssignableToTenant: true,
|
||||
isAccessControlPermission: false,
|
||||
},
|
||||
{
|
||||
permission: Permission.CanEditTelemetryServiceLog,
|
||||
title: 'Can Edit Telemetry Service Log',
|
||||
description:
|
||||
'This permission can edit Telemetry Service Log of this project.',
|
||||
isAssignableToTenant: true,
|
||||
isAccessControlPermission: false,
|
||||
},
|
||||
{
|
||||
permission: Permission.CanReadTelemetryServiceLog,
|
||||
title: 'Can Read Telemetry Service Log',
|
||||
description:
|
||||
'This permission can read Telemetry Service Log of this project.',
|
||||
isAssignableToTenant: true,
|
||||
isAccessControlPermission: false,
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
permission: Permission.CanCreateScheduledMaintenanceOwnerTeam,
|
||||
title: 'Can Create Scheduled Maintenance Team Owner',
|
||||
|
@ -11,6 +11,7 @@ import Hostname from '../Types/API/Hostname';
|
||||
import Route from '../Types/API/Route';
|
||||
import BaseModel from '../Models/BaseModel';
|
||||
import Dictionary from '../Types/Dictionary';
|
||||
import AnalyticsBaseModel from '../AnalyticsModels/BaseModel';
|
||||
|
||||
export default class API {
|
||||
private _protocol: Protocol = Protocol.HTTPS;
|
||||
@ -178,7 +179,13 @@ export default class API {
|
||||
}
|
||||
|
||||
public static async get<
|
||||
T extends JSONObject | JSONArray | BaseModel | Array<BaseModel>
|
||||
T extends
|
||||
| JSONObject
|
||||
| JSONArray
|
||||
| BaseModel
|
||||
| Array<BaseModel>
|
||||
| AnalyticsBaseModel
|
||||
| Array<AnalyticsBaseModel>
|
||||
>(
|
||||
url: URL,
|
||||
data?: JSONObject | JSONArray,
|
||||
@ -188,7 +195,13 @@ export default class API {
|
||||
}
|
||||
|
||||
public static async delete<
|
||||
T extends JSONObject | JSONArray | BaseModel | Array<BaseModel>
|
||||
T extends
|
||||
| JSONObject
|
||||
| JSONArray
|
||||
| BaseModel
|
||||
| Array<BaseModel>
|
||||
| AnalyticsBaseModel
|
||||
| Array<AnalyticsBaseModel>
|
||||
>(
|
||||
url: URL,
|
||||
data?: JSONObject | JSONArray,
|
||||
@ -198,7 +211,13 @@ export default class API {
|
||||
}
|
||||
|
||||
public static async head<
|
||||
T extends JSONObject | JSONArray | BaseModel | Array<BaseModel>
|
||||
T extends
|
||||
| JSONObject
|
||||
| JSONArray
|
||||
| BaseModel
|
||||
| Array<BaseModel>
|
||||
| AnalyticsBaseModel
|
||||
| Array<AnalyticsBaseModel>
|
||||
>(
|
||||
url: URL,
|
||||
data?: JSONObject | JSONArray,
|
||||
@ -208,7 +227,13 @@ export default class API {
|
||||
}
|
||||
|
||||
public static async put<
|
||||
T extends JSONObject | JSONArray | BaseModel | Array<BaseModel>
|
||||
T extends
|
||||
| JSONObject
|
||||
| JSONArray
|
||||
| BaseModel
|
||||
| Array<BaseModel>
|
||||
| AnalyticsBaseModel
|
||||
| Array<AnalyticsBaseModel>
|
||||
>(
|
||||
url: URL,
|
||||
data?: JSONObject | JSONArray,
|
||||
@ -218,7 +243,13 @@ export default class API {
|
||||
}
|
||||
|
||||
public static async post<
|
||||
T extends JSONObject | JSONArray | BaseModel | Array<BaseModel>
|
||||
T extends
|
||||
| JSONObject
|
||||
| JSONArray
|
||||
| BaseModel
|
||||
| Array<BaseModel>
|
||||
| AnalyticsBaseModel
|
||||
| Array<AnalyticsBaseModel>
|
||||
>(
|
||||
url: URL,
|
||||
data?: JSONObject | JSONArray,
|
||||
@ -228,7 +259,13 @@ export default class API {
|
||||
}
|
||||
|
||||
public static async fetch<
|
||||
T extends JSONObject | JSONArray | BaseModel | Array<BaseModel>
|
||||
T extends
|
||||
| JSONObject
|
||||
| JSONArray
|
||||
| BaseModel
|
||||
| Array<BaseModel>
|
||||
| AnalyticsBaseModel
|
||||
| Array<AnalyticsBaseModel>
|
||||
>(
|
||||
method: HTTPMethod,
|
||||
url: URL,
|
||||
|
39
Common/Utils/Realtime.ts
Normal file
39
Common/Utils/Realtime.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import DatabaseType from '../Types/BaseDatabase/DatabaseType';
|
||||
import { JSONObject } from '../Types/JSON';
|
||||
import ObjectID from '../Types/ObjectID';
|
||||
|
||||
export enum EventName {
|
||||
ListenToModalEvent = 'ListenToModelEvent',
|
||||
}
|
||||
|
||||
export enum ModelEventType {
|
||||
Create = 'Create',
|
||||
Update = 'Update',
|
||||
Delete = 'Delete',
|
||||
}
|
||||
|
||||
export interface ListenToModelEventJSON {
|
||||
modelName: string;
|
||||
modelType: DatabaseType;
|
||||
query: JSONObject;
|
||||
eventType: ModelEventType;
|
||||
tenantId: string;
|
||||
select: JSONObject;
|
||||
}
|
||||
|
||||
export interface EnableRealtimeEventsOn {
|
||||
create?: boolean | undefined;
|
||||
update?: boolean | undefined;
|
||||
delete?: boolean | undefined;
|
||||
read?: boolean | undefined;
|
||||
}
|
||||
|
||||
export default class RealtimeUtil {
|
||||
public static getRoomId(
|
||||
tenantId: string | ObjectID,
|
||||
modelName: string,
|
||||
eventType: ModelEventType
|
||||
): string {
|
||||
return tenantId.toString() + '-' + modelName + '-' + eventType;
|
||||
}
|
||||
}
|
@ -25,10 +25,7 @@ import {
|
||||
} from 'Common/Types/Database/LimitMax';
|
||||
import PartialEntity from 'Common/Types/Database/PartialEntity';
|
||||
import { UserPermission } from 'Common/Types/Permission';
|
||||
import { IsBillingEnabled } from '../EnvironmentConfig';
|
||||
import ProjectService from '../Services/ProjectService';
|
||||
import { PlanSelect } from 'Common/Types/Billing/SubscriptionPlan';
|
||||
import UserType from 'Common/Types/UserType';
|
||||
import CommonAPI from './CommonAPI';
|
||||
|
||||
export default class BaseAPI<
|
||||
TBaseModel extends BaseModel,
|
||||
@ -192,7 +189,7 @@ export default class BaseAPI<
|
||||
const permissions: Array<UserPermission> = [];
|
||||
|
||||
const props: DatabaseCommonInteractionProps =
|
||||
await this.getDatabaseCommonInteractionProps(req);
|
||||
await CommonAPI.getDatabaseCommonInteractionProps(req);
|
||||
|
||||
if (
|
||||
props &&
|
||||
@ -217,63 +214,6 @@ export default class BaseAPI<
|
||||
return null;
|
||||
}
|
||||
|
||||
public async getDatabaseCommonInteractionProps(
|
||||
req: ExpressRequest
|
||||
): Promise<DatabaseCommonInteractionProps> {
|
||||
const props: DatabaseCommonInteractionProps = {
|
||||
tenantId: undefined,
|
||||
userGlobalAccessPermission: undefined,
|
||||
userTenantAccessPermission: undefined,
|
||||
userId: undefined,
|
||||
userType: (req as OneUptimeRequest).userType,
|
||||
isMultiTenantRequest: undefined,
|
||||
};
|
||||
|
||||
if (
|
||||
(req as OneUptimeRequest).userAuthorization &&
|
||||
(req as OneUptimeRequest).userAuthorization?.userId
|
||||
) {
|
||||
props.userId = (req as OneUptimeRequest).userAuthorization!.userId;
|
||||
}
|
||||
|
||||
if ((req as OneUptimeRequest).userGlobalAccessPermission) {
|
||||
props.userGlobalAccessPermission = (
|
||||
req as OneUptimeRequest
|
||||
).userGlobalAccessPermission;
|
||||
}
|
||||
|
||||
if ((req as OneUptimeRequest).userTenantAccessPermission) {
|
||||
props.userTenantAccessPermission = (
|
||||
req as OneUptimeRequest
|
||||
).userTenantAccessPermission;
|
||||
}
|
||||
|
||||
if ((req as OneUptimeRequest).tenantId) {
|
||||
props.tenantId = (req as OneUptimeRequest).tenantId || undefined;
|
||||
}
|
||||
|
||||
if (req.headers['is-multi-tenant-query']) {
|
||||
props.isMultiTenantRequest = true;
|
||||
}
|
||||
|
||||
if (IsBillingEnabled && props.tenantId) {
|
||||
const plan: {
|
||||
plan: PlanSelect | null;
|
||||
isSubscriptionUnpaid: boolean;
|
||||
} = await ProjectService.getCurrentPlan(props.tenantId!);
|
||||
props.currentPlan = plan.plan || undefined;
|
||||
props.isSubscriptionUnpaid = plan.isSubscriptionUnpaid;
|
||||
}
|
||||
|
||||
// check for root permissions.
|
||||
|
||||
if (props.userType === UserType.MasterAdmin) {
|
||||
props.isMasterAdmin = true;
|
||||
}
|
||||
|
||||
return props;
|
||||
}
|
||||
|
||||
public async getList(
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse
|
||||
@ -313,7 +253,7 @@ export default class BaseAPI<
|
||||
}
|
||||
|
||||
const databaseProps: DatabaseCommonInteractionProps =
|
||||
await this.getDatabaseCommonInteractionProps(req);
|
||||
await CommonAPI.getDatabaseCommonInteractionProps(req);
|
||||
|
||||
const list: Array<BaseModel> = await this.service.findBy({
|
||||
query,
|
||||
@ -353,7 +293,7 @@ export default class BaseAPI<
|
||||
}
|
||||
|
||||
const databaseProps: DatabaseCommonInteractionProps =
|
||||
await this.getDatabaseCommonInteractionProps(req);
|
||||
await CommonAPI.getDatabaseCommonInteractionProps(req);
|
||||
|
||||
const count: PositiveNumber = await this.service.countBy({
|
||||
query,
|
||||
@ -382,7 +322,7 @@ export default class BaseAPI<
|
||||
const item: BaseModel | null = await this.service.findOneById({
|
||||
id: objectId,
|
||||
select,
|
||||
props: await this.getDatabaseCommonInteractionProps(req),
|
||||
props: await CommonAPI.getDatabaseCommonInteractionProps(req),
|
||||
});
|
||||
|
||||
return Response.sendEntityResponse(req, res, item, this.entityType);
|
||||
@ -399,7 +339,7 @@ export default class BaseAPI<
|
||||
query: {
|
||||
_id: objectId.toString(),
|
||||
},
|
||||
props: await this.getDatabaseCommonInteractionProps(req),
|
||||
props: await CommonAPI.getDatabaseCommonInteractionProps(req),
|
||||
});
|
||||
|
||||
return Response.sendEmptyResponse(req, res);
|
||||
@ -427,7 +367,7 @@ export default class BaseAPI<
|
||||
_id: objectIdString,
|
||||
},
|
||||
data: item,
|
||||
props: await this.getDatabaseCommonInteractionProps(req),
|
||||
props: await CommonAPI.getDatabaseCommonInteractionProps(req),
|
||||
});
|
||||
|
||||
return Response.sendEmptyResponse(req, res);
|
||||
@ -440,7 +380,7 @@ export default class BaseAPI<
|
||||
await this.onBeforeCreate(req, res);
|
||||
const body: JSONObject = req.body;
|
||||
|
||||
const item: TBaseModel = JSONFunctions.fromJSON<TBaseModel>(
|
||||
const item: TBaseModel = BaseModel.fromJSON<TBaseModel>(
|
||||
body['data'] as JSONObject,
|
||||
this.entityType
|
||||
) as TBaseModel;
|
||||
@ -452,7 +392,7 @@ export default class BaseAPI<
|
||||
const createBy: CreateBy<TBaseModel> = {
|
||||
data: item,
|
||||
miscDataProps: miscDataProps,
|
||||
props: await this.getDatabaseCommonInteractionProps(req),
|
||||
props: await CommonAPI.getDatabaseCommonInteractionProps(req),
|
||||
};
|
||||
|
||||
const savedItem: BaseModel = await this.service.create(createBy);
|
||||
|
455
CommonServer/API/BaseAnalyticsAPI.ts
Normal file
455
CommonServer/API/BaseAnalyticsAPI.ts
Normal file
@ -0,0 +1,455 @@
|
||||
import Express, {
|
||||
ExpressRequest,
|
||||
ExpressResponse,
|
||||
ExpressRouter,
|
||||
NextFunction,
|
||||
OneUptimeRequest,
|
||||
} from '../Utils/Express';
|
||||
import UserMiddleware from '../Middleware/UserAuthorization';
|
||||
import PositiveNumber from 'Common/Types/PositiveNumber';
|
||||
import BadRequestException from 'Common/Types/Exception/BadRequestException';
|
||||
import Response from '../Utils/Response';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
import { JSONObject } from 'Common/Types/JSON';
|
||||
import JSONFunctions from 'Common/Types/JSONFunctions';
|
||||
import CreateBy from '../Types/AnalyticsDatabase/CreateBy';
|
||||
import DatabaseCommonInteractionProps from 'Common/Types/BaseDatabase/DatabaseCommonInteractionProps';
|
||||
import Query from '../Types/AnalyticsDatabase/Query';
|
||||
import Select from '../Types/AnalyticsDatabase/Select';
|
||||
import Sort from '../Types/AnalyticsDatabase/Sort';
|
||||
import {
|
||||
DEFAULT_LIMIT,
|
||||
LIMIT_PER_PROJECT,
|
||||
} from 'Common/Types/Database/LimitMax';
|
||||
import { UserPermission } from 'Common/Types/Permission';
|
||||
import AnalyticsDataModel from 'Common/AnalyticsModels/BaseModel';
|
||||
import AnalyticsDatabaseService from '../Services/AnalyticsDatabaseService';
|
||||
import CommonAPI from './CommonAPI';
|
||||
|
||||
export default class BaseAnalyticsAPI<
|
||||
TAnalyticsDataModel extends AnalyticsDataModel,
|
||||
TBaseService extends AnalyticsDatabaseService<AnalyticsDataModel>
|
||||
> {
|
||||
public entityType: { new (): TAnalyticsDataModel };
|
||||
|
||||
public router: ExpressRouter;
|
||||
public service: TBaseService;
|
||||
|
||||
public constructor(
|
||||
type: { new (): TAnalyticsDataModel },
|
||||
service: TBaseService
|
||||
) {
|
||||
this.entityType = type;
|
||||
const router: ExpressRouter = Express.getRouter();
|
||||
// Create
|
||||
router.post(
|
||||
`${new this.entityType().crudApiPath.toString()}`,
|
||||
UserMiddleware.getUserMiddleware,
|
||||
async (
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse,
|
||||
next: NextFunction
|
||||
) => {
|
||||
try {
|
||||
await this.createItem(req, res);
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// List
|
||||
router.post(
|
||||
`${new this.entityType().crudApiPath?.toString()}/get-list`,
|
||||
UserMiddleware.getUserMiddleware,
|
||||
async (
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse,
|
||||
next: NextFunction
|
||||
) => {
|
||||
try {
|
||||
await this.getList(req, res);
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// List
|
||||
router.get(
|
||||
`${new this.entityType().crudApiPath?.toString()}/get-list`,
|
||||
UserMiddleware.getUserMiddleware,
|
||||
async (
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse,
|
||||
next: NextFunction
|
||||
) => {
|
||||
try {
|
||||
await this.getList(req, res);
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// count
|
||||
router.post(
|
||||
`${new this.entityType().crudApiPath?.toString()}/count`,
|
||||
UserMiddleware.getUserMiddleware,
|
||||
async (
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse,
|
||||
next: NextFunction
|
||||
) => {
|
||||
try {
|
||||
await this.count(req, res);
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Get Item
|
||||
router.post(
|
||||
`${new this.entityType().crudApiPath?.toString()}/:id/get-item`,
|
||||
UserMiddleware.getUserMiddleware,
|
||||
async (
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse,
|
||||
next: NextFunction
|
||||
) => {
|
||||
try {
|
||||
await this.getItem(req, res);
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Get Item
|
||||
router.get(
|
||||
`${new this.entityType().crudApiPath?.toString()}/:id/get-item`,
|
||||
UserMiddleware.getUserMiddleware,
|
||||
async (
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse,
|
||||
next: NextFunction
|
||||
) => {
|
||||
try {
|
||||
await this.getItem(req, res);
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Update
|
||||
router.put(
|
||||
`${new this.entityType().crudApiPath?.toString()}/:id`,
|
||||
UserMiddleware.getUserMiddleware,
|
||||
async (
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse,
|
||||
next: NextFunction
|
||||
) => {
|
||||
try {
|
||||
await this.updateItem(req, res);
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Delete
|
||||
router.delete(
|
||||
`${new this.entityType().crudApiPath?.toString()}/:id`,
|
||||
UserMiddleware.getUserMiddleware,
|
||||
async (
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse,
|
||||
next: NextFunction
|
||||
) => {
|
||||
try {
|
||||
await this.deleteItem(req, res);
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
this.router = router;
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
public async getPermissionsForTenant(
|
||||
req: ExpressRequest
|
||||
): Promise<Array<UserPermission>> {
|
||||
const permissions: Array<UserPermission> = [];
|
||||
|
||||
const props: DatabaseCommonInteractionProps =
|
||||
await CommonAPI.getDatabaseCommonInteractionProps(req);
|
||||
|
||||
if (
|
||||
props &&
|
||||
props.userTenantAccessPermission &&
|
||||
props.userTenantAccessPermission[props.tenantId?.toString() || '']
|
||||
) {
|
||||
return (
|
||||
props.userTenantAccessPermission[
|
||||
props.tenantId?.toString() || ''
|
||||
]?.permissions || []
|
||||
);
|
||||
}
|
||||
|
||||
return permissions;
|
||||
}
|
||||
|
||||
public getTenantId(req: ExpressRequest): ObjectID | null {
|
||||
if ((req as OneUptimeRequest).tenantId) {
|
||||
return (req as OneUptimeRequest).tenantId as ObjectID;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public async getList(
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse
|
||||
): Promise<void> {
|
||||
await this.onBeforeList(req, res);
|
||||
|
||||
const skip: PositiveNumber = req.query['skip']
|
||||
? new PositiveNumber(req.query['skip'] as string)
|
||||
: new PositiveNumber(0);
|
||||
|
||||
const limit: PositiveNumber = req.query['limit']
|
||||
? new PositiveNumber(req.query['limit'] as string)
|
||||
: new PositiveNumber(DEFAULT_LIMIT);
|
||||
|
||||
if (limit.toNumber() > LIMIT_PER_PROJECT) {
|
||||
throw new BadRequestException(
|
||||
'Limit should be less than ' + LIMIT_PER_PROJECT
|
||||
);
|
||||
}
|
||||
|
||||
let query: Query<AnalyticsDataModel> = {};
|
||||
let select: Select<AnalyticsDataModel> = {};
|
||||
let sort: Sort<AnalyticsDataModel> = {};
|
||||
|
||||
if (req.body) {
|
||||
query = JSONFunctions.deserialize(
|
||||
req.body['query']
|
||||
) as Query<AnalyticsDataModel>;
|
||||
|
||||
select = JSONFunctions.deserialize(
|
||||
req.body['select']
|
||||
) as Select<AnalyticsDataModel>;
|
||||
|
||||
sort = JSONFunctions.deserialize(
|
||||
req.body['sort']
|
||||
) as Sort<AnalyticsDataModel>;
|
||||
}
|
||||
|
||||
const databaseProps: DatabaseCommonInteractionProps =
|
||||
await CommonAPI.getDatabaseCommonInteractionProps(req);
|
||||
|
||||
const list: Array<AnalyticsDataModel> = await this.service.findBy({
|
||||
query,
|
||||
select,
|
||||
skip: skip,
|
||||
limit: limit,
|
||||
sort: sort,
|
||||
props: databaseProps,
|
||||
});
|
||||
|
||||
const count: PositiveNumber = await this.service.countBy({
|
||||
query,
|
||||
props: databaseProps,
|
||||
});
|
||||
|
||||
return Response.sendEntityArrayResponse(
|
||||
req,
|
||||
res,
|
||||
list,
|
||||
count,
|
||||
this.entityType
|
||||
);
|
||||
}
|
||||
|
||||
public async count(
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse
|
||||
): Promise<void> {
|
||||
let query: Query<AnalyticsDataModel> = {};
|
||||
|
||||
await this.onBeforeCount(req, res);
|
||||
|
||||
if (req.body) {
|
||||
query = JSONFunctions.deserialize(
|
||||
req.body['query']
|
||||
) as Query<AnalyticsDataModel>;
|
||||
}
|
||||
|
||||
const databaseProps: DatabaseCommonInteractionProps =
|
||||
await CommonAPI.getDatabaseCommonInteractionProps(req);
|
||||
|
||||
const count: PositiveNumber = await this.service.countBy({
|
||||
query,
|
||||
props: databaseProps,
|
||||
});
|
||||
|
||||
return Response.sendJsonObjectResponse(req, res, {
|
||||
count: count.toNumber(),
|
||||
});
|
||||
}
|
||||
|
||||
public async getItem(
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse
|
||||
): Promise<void> {
|
||||
const objectId: ObjectID = new ObjectID(req.params['id'] as string);
|
||||
await this.onBeforeGet(req, res);
|
||||
let select: Select<AnalyticsDataModel> = {};
|
||||
|
||||
if (req.body) {
|
||||
select = JSONFunctions.deserialize(
|
||||
req.body['select']
|
||||
) as Select<AnalyticsDataModel>;
|
||||
}
|
||||
|
||||
const item: AnalyticsDataModel | null = await this.service.findOneById({
|
||||
id: objectId,
|
||||
select,
|
||||
props: await CommonAPI.getDatabaseCommonInteractionProps(req),
|
||||
});
|
||||
|
||||
return Response.sendEntityResponse(req, res, item, this.entityType);
|
||||
}
|
||||
|
||||
public async deleteItem(
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse
|
||||
): Promise<void> {
|
||||
await this.onBeforeDelete(req, res);
|
||||
const objectId: ObjectID = new ObjectID(req.params['id'] as string);
|
||||
|
||||
await this.service.deleteOneBy({
|
||||
query: {
|
||||
_id: objectId.toString(),
|
||||
},
|
||||
props: await CommonAPI.getDatabaseCommonInteractionProps(req),
|
||||
});
|
||||
|
||||
return Response.sendEmptyResponse(req, res);
|
||||
}
|
||||
|
||||
public async updateItem(
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse
|
||||
): Promise<void> {
|
||||
await this.onBeforeUpdate(req, res);
|
||||
const objectId: ObjectID = new ObjectID(req.params['id'] as string);
|
||||
const objectIdString: string = objectId.toString();
|
||||
const body: JSONObject = req.body;
|
||||
|
||||
const item: TAnalyticsDataModel =
|
||||
AnalyticsDataModel.fromJSON<TAnalyticsDataModel>(
|
||||
body['data'] as JSONObject,
|
||||
this.entityType
|
||||
) as TAnalyticsDataModel;
|
||||
|
||||
delete (item as any)['_id'];
|
||||
delete (item as any)['createdAt'];
|
||||
delete (item as any)['updatedAt'];
|
||||
|
||||
await this.service.updateOneBy({
|
||||
query: {
|
||||
_id: objectIdString,
|
||||
},
|
||||
data: item,
|
||||
props: await CommonAPI.getDatabaseCommonInteractionProps(req),
|
||||
});
|
||||
|
||||
return Response.sendEmptyResponse(req, res);
|
||||
}
|
||||
|
||||
public async createItem(
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse
|
||||
): Promise<void> {
|
||||
await this.onBeforeCreate(req, res);
|
||||
const body: JSONObject = req.body;
|
||||
|
||||
const item: TAnalyticsDataModel =
|
||||
AnalyticsDataModel.fromJSON<TAnalyticsDataModel>(
|
||||
body['data'] as JSONObject,
|
||||
this.entityType
|
||||
) as TAnalyticsDataModel;
|
||||
|
||||
const createBy: CreateBy<TAnalyticsDataModel> = {
|
||||
data: item,
|
||||
props: await CommonAPI.getDatabaseCommonInteractionProps(req),
|
||||
};
|
||||
|
||||
const savedItem: AnalyticsDataModel = await this.service.create(
|
||||
createBy
|
||||
);
|
||||
|
||||
return Response.sendEntityResponse(
|
||||
req,
|
||||
res,
|
||||
savedItem,
|
||||
this.entityType
|
||||
);
|
||||
}
|
||||
|
||||
public getRouter(): ExpressRouter {
|
||||
return this.router;
|
||||
}
|
||||
|
||||
public getEntityName(): string {
|
||||
return this.entityType.name;
|
||||
}
|
||||
|
||||
protected async onBeforeList(
|
||||
_req: ExpressRequest,
|
||||
_res: ExpressResponse
|
||||
): Promise<any> {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
protected async onBeforeCreate(
|
||||
_req: ExpressRequest,
|
||||
_res: ExpressResponse
|
||||
): Promise<any> {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
protected async onBeforeGet(
|
||||
_req: ExpressRequest,
|
||||
_res: ExpressResponse
|
||||
): Promise<any> {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
protected async onBeforeUpdate(
|
||||
_req: ExpressRequest,
|
||||
_res: ExpressResponse
|
||||
): Promise<any> {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
protected async onBeforeDelete(
|
||||
_req: ExpressRequest,
|
||||
_res: ExpressResponse
|
||||
): Promise<any> {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
protected async onBeforeCount(
|
||||
_req: ExpressRequest,
|
||||
_res: ExpressResponse
|
||||
): Promise<any> {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
import BadDataException from 'Common/Types/Exception/BadDataException';
|
||||
import { JSONObject } from 'Common/Types/JSON';
|
||||
import JSONFunctions from 'Common/Types/JSONFunctions';
|
||||
import Permission, { UserPermission } from 'Common/Types/Permission';
|
||||
import BillingInvoice from 'Model/Models/BillingInvoice';
|
||||
import Project from 'Model/Models/Project';
|
||||
@ -19,6 +18,7 @@ import {
|
||||
import Response from '../Utils/Response';
|
||||
import BaseAPI from './BaseAPI';
|
||||
import SubscriptionStatus from 'Common/Types/Billing/SubscriptionStatus';
|
||||
import BaseModel from 'Common/Models/BaseModel';
|
||||
|
||||
export default class UserAPI extends BaseAPI<
|
||||
BillingInvoice,
|
||||
@ -98,7 +98,7 @@ export default class UserAPI extends BaseAPI<
|
||||
const body: JSONObject = req.body;
|
||||
|
||||
const item: BillingInvoice =
|
||||
JSONFunctions.fromJSON<BillingInvoice>(
|
||||
BaseModel.fromJSON<BillingInvoice>(
|
||||
body['data'] as JSONObject,
|
||||
this.entityType
|
||||
) as BillingInvoice;
|
||||
|
65
CommonServer/API/CommonAPI.ts
Normal file
65
CommonServer/API/CommonAPI.ts
Normal file
@ -0,0 +1,65 @@
|
||||
import { IsBillingEnabled } from '../EnvironmentConfig';
|
||||
import ProjectService from '../Services/ProjectService';
|
||||
import { PlanSelect } from 'Common/Types/Billing/SubscriptionPlan';
|
||||
import UserType from 'Common/Types/UserType';
|
||||
import { ExpressRequest, OneUptimeRequest } from '../Utils/Express';
|
||||
import DatabaseCommonInteractionProps from 'Common/Types/BaseDatabase/DatabaseCommonInteractionProps';
|
||||
|
||||
export default class CommonAPI {
|
||||
public static async getDatabaseCommonInteractionProps(
|
||||
req: ExpressRequest
|
||||
): Promise<DatabaseCommonInteractionProps> {
|
||||
const props: DatabaseCommonInteractionProps = {
|
||||
tenantId: undefined,
|
||||
userGlobalAccessPermission: undefined,
|
||||
userTenantAccessPermission: undefined,
|
||||
userId: undefined,
|
||||
userType: (req as OneUptimeRequest).userType,
|
||||
isMultiTenantRequest: undefined,
|
||||
};
|
||||
|
||||
if (
|
||||
(req as OneUptimeRequest).userAuthorization &&
|
||||
(req as OneUptimeRequest).userAuthorization?.userId
|
||||
) {
|
||||
props.userId = (req as OneUptimeRequest).userAuthorization!.userId;
|
||||
}
|
||||
|
||||
if ((req as OneUptimeRequest).userGlobalAccessPermission) {
|
||||
props.userGlobalAccessPermission = (
|
||||
req as OneUptimeRequest
|
||||
).userGlobalAccessPermission;
|
||||
}
|
||||
|
||||
if ((req as OneUptimeRequest).userTenantAccessPermission) {
|
||||
props.userTenantAccessPermission = (
|
||||
req as OneUptimeRequest
|
||||
).userTenantAccessPermission;
|
||||
}
|
||||
|
||||
if ((req as OneUptimeRequest).tenantId) {
|
||||
props.tenantId = (req as OneUptimeRequest).tenantId || undefined;
|
||||
}
|
||||
|
||||
if (req.headers['is-multi-tenant-query']) {
|
||||
props.isMultiTenantRequest = true;
|
||||
}
|
||||
|
||||
if (IsBillingEnabled && props.tenantId) {
|
||||
const plan: {
|
||||
plan: PlanSelect | null;
|
||||
isSubscriptionUnpaid: boolean;
|
||||
} = await ProjectService.getCurrentPlan(props.tenantId!);
|
||||
props.currentPlan = plan.plan || undefined;
|
||||
props.isSubscriptionUnpaid = plan.isSubscriptionUnpaid;
|
||||
}
|
||||
|
||||
// check for root permissions.
|
||||
|
||||
if (props.userType === UserType.MasterAdmin) {
|
||||
props.isMasterAdmin = true;
|
||||
}
|
||||
|
||||
return props;
|
||||
}
|
||||
}
|
@ -15,6 +15,7 @@ import BadDataException from 'Common/Types/Exception/BadDataException';
|
||||
import MonitorStatus from 'Model/Models/MonitorStatus';
|
||||
import MonitorStatusTimeline from 'Model/Models/MonitorStatusTimeline';
|
||||
import OneUptimeDate from 'Common/Types/Date';
|
||||
import CommonAPI from './CommonAPI';
|
||||
|
||||
export default class MonitorGroupAPI extends BaseAPI<
|
||||
MonitorGroup,
|
||||
@ -47,7 +48,9 @@ export default class MonitorGroupAPI extends BaseAPI<
|
||||
new ObjectID(
|
||||
req.params['monitorGroupId'].toString()
|
||||
),
|
||||
await this.getDatabaseCommonInteractionProps(req)
|
||||
await CommonAPI.getDatabaseCommonInteractionProps(
|
||||
req
|
||||
)
|
||||
);
|
||||
|
||||
return Response.sendEntityResponse(
|
||||
@ -91,7 +94,9 @@ export default class MonitorGroupAPI extends BaseAPI<
|
||||
),
|
||||
startDate,
|
||||
endDate,
|
||||
await this.getDatabaseCommonInteractionProps(req)
|
||||
await CommonAPI.getDatabaseCommonInteractionProps(
|
||||
req
|
||||
)
|
||||
);
|
||||
|
||||
return Response.sendEntityArrayResponse(
|
||||
|
@ -69,6 +69,8 @@ import IncidentState from 'Model/Models/IncidentState';
|
||||
import IncidentStateService from '../Services/IncidentStateService';
|
||||
import ScheduledMaintenanceState from 'Model/Models/ScheduledMaintenanceState';
|
||||
import ScheduledMaintenanceStateService from '../Services/ScheduledMaintenanceStateService';
|
||||
import BaseModel from 'Common/Models/BaseModel';
|
||||
import CommonAPI from './CommonAPI';
|
||||
|
||||
export default class StatusPageAPI extends BaseAPI<
|
||||
StatusPage,
|
||||
@ -324,12 +326,12 @@ export default class StatusPageAPI extends BaseAPI<
|
||||
});
|
||||
|
||||
const response: JSONObject = {
|
||||
statusPage: JSONFunctions.toJSON(item, StatusPage),
|
||||
footerLinks: JSONFunctions.toJSONArray(
|
||||
statusPage: BaseModel.toJSON(item, StatusPage),
|
||||
footerLinks: BaseModel.toJSONArray(
|
||||
footerLinks,
|
||||
StatusPageFooterLink
|
||||
),
|
||||
headerLinks: JSONFunctions.toJSONArray(
|
||||
headerLinks: BaseModel.toJSONArray(
|
||||
headerLinks,
|
||||
StatusPageHeaderLink
|
||||
),
|
||||
@ -411,7 +413,9 @@ export default class StatusPageAPI extends BaseAPI<
|
||||
if (
|
||||
!(await this.service.hasReadAccess(
|
||||
objectId,
|
||||
await this.getDatabaseCommonInteractionProps(req),
|
||||
await CommonAPI.getDatabaseCommonInteractionProps(
|
||||
req
|
||||
),
|
||||
req
|
||||
))
|
||||
) {
|
||||
@ -986,52 +990,52 @@ export default class StatusPageAPI extends BaseAPI<
|
||||
|
||||
const response: JSONObject = {
|
||||
scheduledMaintenanceEventsPublicNotes:
|
||||
JSONFunctions.toJSONArray(
|
||||
BaseModel.toJSONArray(
|
||||
scheduledMaintenanceEventsPublicNotes,
|
||||
ScheduledMaintenancePublicNote
|
||||
),
|
||||
scheduledMaintenanceEvents: JSONFunctions.toJSONArray(
|
||||
scheduledMaintenanceEvents: BaseModel.toJSONArray(
|
||||
scheduledMaintenanceEvents,
|
||||
ScheduledMaintenance
|
||||
),
|
||||
activeAnnouncements: JSONFunctions.toJSONArray(
|
||||
activeAnnouncements: BaseModel.toJSONArray(
|
||||
activeAnnouncements,
|
||||
StatusPageAnnouncement
|
||||
),
|
||||
incidentPublicNotes: JSONFunctions.toJSONArray(
|
||||
incidentPublicNotes: BaseModel.toJSONArray(
|
||||
incidentPublicNotes,
|
||||
IncidentPublicNote
|
||||
),
|
||||
activeIncidents: JSONFunctions.toJSONArray(
|
||||
activeIncidents: BaseModel.toJSONArray(
|
||||
activeIncidents,
|
||||
Incident
|
||||
),
|
||||
monitorStatusTimelines: JSONFunctions.toJSONArray(
|
||||
monitorStatusTimelines: BaseModel.toJSONArray(
|
||||
monitorStatusTimelines,
|
||||
MonitorStatusTimeline
|
||||
),
|
||||
resourceGroups: JSONFunctions.toJSONArray(
|
||||
resourceGroups: BaseModel.toJSONArray(
|
||||
groups,
|
||||
StatusPageGroup
|
||||
),
|
||||
monitorStatuses: JSONFunctions.toJSONArray(
|
||||
monitorStatuses: BaseModel.toJSONArray(
|
||||
monitorStatuses,
|
||||
MonitorStatus
|
||||
),
|
||||
statusPageResources: JSONFunctions.toJSONArray(
|
||||
statusPageResources: BaseModel.toJSONArray(
|
||||
statusPageResources,
|
||||
StatusPageResource
|
||||
),
|
||||
incidentStateTimelines: JSONFunctions.toJSONArray(
|
||||
incidentStateTimelines: BaseModel.toJSONArray(
|
||||
incidentStateTimelines,
|
||||
IncidentStateTimeline
|
||||
),
|
||||
statusPage: JSONFunctions.toJSONObject(
|
||||
statusPage: BaseModel.toJSONObject(
|
||||
statusPage,
|
||||
StatusPage
|
||||
),
|
||||
scheduledMaintenanceStateTimelines:
|
||||
JSONFunctions.toJSONArray(
|
||||
BaseModel.toJSONArray(
|
||||
scheduledMaintenanceStateTimelines,
|
||||
ScheduledMaintenanceStateTimeline
|
||||
),
|
||||
@ -1068,7 +1072,9 @@ export default class StatusPageAPI extends BaseAPI<
|
||||
if (
|
||||
!(await this.service.hasReadAccess(
|
||||
objectId,
|
||||
await this.getDatabaseCommonInteractionProps(req),
|
||||
await CommonAPI.getDatabaseCommonInteractionProps(
|
||||
req
|
||||
),
|
||||
req
|
||||
))
|
||||
) {
|
||||
@ -1144,7 +1150,7 @@ export default class StatusPageAPI extends BaseAPI<
|
||||
const response: JSONObject = await this.getIncidents(
|
||||
objectId,
|
||||
null,
|
||||
await this.getDatabaseCommonInteractionProps(req),
|
||||
await CommonAPI.getDatabaseCommonInteractionProps(req),
|
||||
req
|
||||
);
|
||||
|
||||
@ -1174,7 +1180,9 @@ export default class StatusPageAPI extends BaseAPI<
|
||||
await this.getScheduledMaintenanceEvents(
|
||||
objectId,
|
||||
null,
|
||||
await this.getDatabaseCommonInteractionProps(req),
|
||||
await CommonAPI.getDatabaseCommonInteractionProps(
|
||||
req
|
||||
),
|
||||
req
|
||||
);
|
||||
|
||||
@ -1203,7 +1211,7 @@ export default class StatusPageAPI extends BaseAPI<
|
||||
const response: JSONObject = await this.getAnnouncements(
|
||||
objectId,
|
||||
null,
|
||||
await this.getDatabaseCommonInteractionProps(req),
|
||||
await CommonAPI.getDatabaseCommonInteractionProps(req),
|
||||
req
|
||||
);
|
||||
|
||||
@ -1236,7 +1244,7 @@ export default class StatusPageAPI extends BaseAPI<
|
||||
const response: JSONObject = await this.getIncidents(
|
||||
objectId,
|
||||
incidentId,
|
||||
await this.getDatabaseCommonInteractionProps(req),
|
||||
await CommonAPI.getDatabaseCommonInteractionProps(req),
|
||||
req
|
||||
);
|
||||
|
||||
@ -1270,7 +1278,9 @@ export default class StatusPageAPI extends BaseAPI<
|
||||
await this.getScheduledMaintenanceEvents(
|
||||
objectId,
|
||||
scheduledMaintenanceId,
|
||||
await this.getDatabaseCommonInteractionProps(req),
|
||||
await CommonAPI.getDatabaseCommonInteractionProps(
|
||||
req
|
||||
),
|
||||
req
|
||||
);
|
||||
|
||||
@ -1303,7 +1313,7 @@ export default class StatusPageAPI extends BaseAPI<
|
||||
const response: JSONObject = await this.getAnnouncements(
|
||||
objectId,
|
||||
announcementId,
|
||||
await this.getDatabaseCommonInteractionProps(req),
|
||||
await CommonAPI.getDatabaseCommonInteractionProps(req),
|
||||
req
|
||||
);
|
||||
|
||||
@ -1619,23 +1629,23 @@ export default class StatusPageAPI extends BaseAPI<
|
||||
});
|
||||
|
||||
const response: JSONObject = {
|
||||
scheduledMaintenanceEventsPublicNotes: JSONFunctions.toJSONArray(
|
||||
scheduledMaintenanceEventsPublicNotes: BaseModel.toJSONArray(
|
||||
scheduledMaintenanceEventsPublicNotes,
|
||||
ScheduledMaintenancePublicNote
|
||||
),
|
||||
scheduledMaintenanceStates: JSONFunctions.toJSONArray(
|
||||
scheduledMaintenanceStates: BaseModel.toJSONArray(
|
||||
scheduledEventStates,
|
||||
ScheduledMaintenanceState
|
||||
),
|
||||
scheduledMaintenanceEvents: JSONFunctions.toJSONArray(
|
||||
scheduledMaintenanceEvents: BaseModel.toJSONArray(
|
||||
scheduledMaintenanceEvents,
|
||||
ScheduledMaintenance
|
||||
),
|
||||
statusPageResources: JSONFunctions.toJSONArray(
|
||||
statusPageResources: BaseModel.toJSONArray(
|
||||
statusPageResources,
|
||||
StatusPageResource
|
||||
),
|
||||
scheduledMaintenanceStateTimelines: JSONFunctions.toJSONArray(
|
||||
scheduledMaintenanceStateTimelines: BaseModel.toJSONArray(
|
||||
scheduledMaintenanceStateTimelines,
|
||||
ScheduledMaintenanceStateTimeline
|
||||
),
|
||||
@ -1742,11 +1752,11 @@ export default class StatusPageAPI extends BaseAPI<
|
||||
});
|
||||
|
||||
const response: JSONObject = {
|
||||
announcements: JSONFunctions.toJSONArray(
|
||||
announcements: BaseModel.toJSONArray(
|
||||
announcements,
|
||||
StatusPageAnnouncement
|
||||
),
|
||||
statusPageResources: JSONFunctions.toJSONArray(
|
||||
statusPageResources: BaseModel.toJSONArray(
|
||||
statusPageResources,
|
||||
StatusPageResource
|
||||
),
|
||||
@ -2049,20 +2059,20 @@ export default class StatusPageAPI extends BaseAPI<
|
||||
});
|
||||
|
||||
const response: JSONObject = {
|
||||
incidentPublicNotes: JSONFunctions.toJSONArray(
|
||||
incidentPublicNotes: BaseModel.toJSONArray(
|
||||
incidentPublicNotes,
|
||||
IncidentPublicNote
|
||||
),
|
||||
incidentStates: JSONFunctions.toJSONArray(
|
||||
incidentStates: BaseModel.toJSONArray(
|
||||
incidentStates,
|
||||
IncidentState
|
||||
),
|
||||
incidents: JSONFunctions.toJSONArray(incidents, Incident),
|
||||
statusPageResources: JSONFunctions.toJSONArray(
|
||||
incidents: BaseModel.toJSONArray(incidents, Incident),
|
||||
statusPageResources: BaseModel.toJSONArray(
|
||||
statusPageResources,
|
||||
StatusPageResource
|
||||
),
|
||||
incidentStateTimelines: JSONFunctions.toJSONArray(
|
||||
incidentStateTimelines: BaseModel.toJSONArray(
|
||||
incidentStateTimelines,
|
||||
IncidentStateTimeline
|
||||
),
|
||||
|
@ -1,25 +1,41 @@
|
||||
import SocketIO from 'socket.io';
|
||||
import http from 'http';
|
||||
import Express, { ExpressApplication } from '../Utils/Express';
|
||||
|
||||
const app: ExpressApplication = Express.getExpressApp();
|
||||
const server: http.Server = http.createServer(app);
|
||||
import Express from '../Utils/Express';
|
||||
import { createAdapter } from '@socket.io/redis-adapter';
|
||||
import Redis, { ClientType } from './Redis';
|
||||
import DatabaseNotConnectedException from 'Common/Types/Exception/DatabaseNotConnectedException';
|
||||
import { RealtimeRoute } from 'Common/ServiceRoute';
|
||||
|
||||
export type Socket = SocketIO.Socket;
|
||||
export type SocketServer = SocketIO.Server;
|
||||
|
||||
const io: SocketIO.Server = new SocketIO.Server(server, {
|
||||
path: '/realtime/socket.io',
|
||||
transports: ['websocket', 'polling'], // Using websocket does not require sticky session
|
||||
perMessageDeflate: {
|
||||
threshold: 1024, // Defaults to 1024
|
||||
zlibDeflateOptions: {
|
||||
chunkSize: 16 * 1024, // Defaults to 16 * 1024
|
||||
},
|
||||
zlibInflateOptions: {
|
||||
windowBits: 15, // Defaults to 15
|
||||
memLevel: 8, // Defaults to 8
|
||||
},
|
||||
},
|
||||
});
|
||||
export default abstract class IO {
|
||||
private static socketServer: SocketIO.Server | null = null;
|
||||
|
||||
export default io;
|
||||
public static init(): void {
|
||||
const server: http.Server = Express.getHttpServer();
|
||||
|
||||
this.socketServer = new SocketIO.Server(server, {
|
||||
path: RealtimeRoute.toString(),
|
||||
});
|
||||
|
||||
if (!Redis.getClient()) {
|
||||
throw new DatabaseNotConnectedException(
|
||||
'Redis is not connected. Please connect to Redis before connecting to SocketIO.'
|
||||
);
|
||||
}
|
||||
|
||||
const pubClient: ClientType = Redis.getClient()!.duplicate();
|
||||
const subClient: ClientType = Redis.getClient()!.duplicate();
|
||||
|
||||
this.socketServer.adapter(createAdapter(pubClient, subClient));
|
||||
}
|
||||
|
||||
public static getSocketServer(): SocketIO.Server | null {
|
||||
if (!this.socketServer) {
|
||||
this.init();
|
||||
}
|
||||
|
||||
return this.socketServer;
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,9 @@ import {
|
||||
OnUpdate,
|
||||
} from '../Types/AnalyticsDatabase/Hooks';
|
||||
import Typeof from 'Common/Types/Typeof';
|
||||
import ModelPermission from '../Types/AnalyticsDatabase/ModelPermission';
|
||||
import ModelPermission, {
|
||||
CheckReadPermissionType,
|
||||
} from '../Types/AnalyticsDatabase/ModelPermission';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
import Exception from 'Common/Types/Exception/Exception';
|
||||
import API from 'Common/Utils/API';
|
||||
@ -32,7 +34,6 @@ import UpdateBy from '../Types/AnalyticsDatabase/UpdateBy';
|
||||
import FindBy from '../Types/AnalyticsDatabase/FindBy';
|
||||
import PositiveNumber from 'Common/Types/PositiveNumber';
|
||||
import SortOrder from 'Common/Types/BaseDatabase/SortOrder';
|
||||
import Query from '../Types/AnalyticsDatabase/Query';
|
||||
import Select from '../Types/AnalyticsDatabase/Select';
|
||||
import { ExecResult } from '@clickhouse/client';
|
||||
import { Stream } from 'node:stream';
|
||||
@ -43,6 +44,11 @@ import FindOneByID from '../Types/AnalyticsDatabase/FindOneByID';
|
||||
import OneUptimeDate from 'Common/Types/Date';
|
||||
import CreateManyBy from '../Types/AnalyticsDatabase/CreateManyBy';
|
||||
import StatementGenerator from '../Utils/AnalyticsDatabase/StatementGenerator';
|
||||
import CountBy from '../Types/AnalyticsDatabase/CountBy';
|
||||
import DeleteOneBy from '../Types/AnalyticsDatabase/DeleteOneBy';
|
||||
import UpdateOneBy from '../Types/AnalyticsDatabase/UpdateOneBy';
|
||||
import Realtime from '../Utils/Realtime';
|
||||
import { ModelEventType } from 'Common/Utils/Realtime';
|
||||
|
||||
export default class AnalyticsDatabaseService<
|
||||
TBaseModel extends AnalyticsBaseModel
|
||||
@ -74,6 +80,40 @@ export default class AnalyticsDatabaseService<
|
||||
});
|
||||
}
|
||||
|
||||
public async countBy(
|
||||
countBy: CountBy<TBaseModel>
|
||||
): Promise<PositiveNumber> {
|
||||
try {
|
||||
const checkReadPermissionType: CheckReadPermissionType<TBaseModel> =
|
||||
await ModelPermission.checkReadPermission(
|
||||
this.modelType,
|
||||
countBy.query,
|
||||
null,
|
||||
countBy.props
|
||||
);
|
||||
|
||||
countBy.query = checkReadPermissionType.query;
|
||||
|
||||
const countStatement: string = this.toCountStatement(countBy);
|
||||
|
||||
const dbResult: ExecResult<Stream> = await this.execute(
|
||||
countStatement
|
||||
);
|
||||
|
||||
const strResult: string = await StreamUtil.convertStreamToText(
|
||||
dbResult.stream
|
||||
);
|
||||
|
||||
let countPositive: PositiveNumber = new PositiveNumber(strResult);
|
||||
|
||||
countPositive = await this.onCountSuccess(countPositive);
|
||||
return countPositive;
|
||||
} catch (error) {
|
||||
await this.onCountError(error as Exception);
|
||||
throw this.getException(error as Exception);
|
||||
}
|
||||
}
|
||||
|
||||
public async findBy(
|
||||
findBy: FindBy<TBaseModel>
|
||||
): Promise<Array<TBaseModel>> {
|
||||
@ -84,6 +124,9 @@ export default class AnalyticsDatabaseService<
|
||||
findBy: FindBy<TBaseModel>
|
||||
): Promise<Array<TBaseModel>> {
|
||||
try {
|
||||
|
||||
debugger;
|
||||
|
||||
if (!findBy.sort || Object.keys(findBy.sort).length === 0) {
|
||||
findBy.sort = {
|
||||
createdAt: SortOrder.Descending,
|
||||
@ -111,10 +154,8 @@ export default class AnalyticsDatabaseService<
|
||||
(onBeforeFind.select as any)['_id'] = true;
|
||||
}
|
||||
|
||||
const result: {
|
||||
query: Query<TBaseModel>;
|
||||
select: Select<TBaseModel> | null;
|
||||
} = await ModelPermission.checkReadPermission(
|
||||
const result: CheckReadPermissionType<TBaseModel> =
|
||||
await ModelPermission.checkReadPermission(
|
||||
this.modelType,
|
||||
onBeforeFind.query,
|
||||
onBeforeFind.select || null,
|
||||
@ -151,8 +192,8 @@ export default class AnalyticsDatabaseService<
|
||||
|
||||
let items: Array<TBaseModel> =
|
||||
AnalyticsBaseModel.fromJSONArray<TBaseModel>(
|
||||
this.modelType,
|
||||
jsonItems
|
||||
jsonItems,
|
||||
this.modelType
|
||||
);
|
||||
|
||||
items = this.sanitizeFindByItems(items, onBeforeFind);
|
||||
@ -229,6 +270,36 @@ export default class AnalyticsDatabaseService<
|
||||
return Promise.resolve({ findBy, carryForward: null });
|
||||
}
|
||||
|
||||
public toCountStatement(countBy: CountBy<TBaseModel>): string {
|
||||
if (!this.database) {
|
||||
this.useDefaultDatabase();
|
||||
}
|
||||
|
||||
let statement: string = `SELECT count() FROM ${
|
||||
this.database.getDatasourceOptions().database
|
||||
}.${this.model.tableName}
|
||||
${
|
||||
Object.keys(countBy.query).length > 0 ? 'WHERE' : ''
|
||||
} ${this.statementGenerator.toWhereStatement(countBy.query)}
|
||||
`;
|
||||
|
||||
if (countBy.limit) {
|
||||
statement += `
|
||||
LIMIT ${countBy.limit.toString()}
|
||||
`;
|
||||
}
|
||||
|
||||
if (countBy.skip) {
|
||||
statement += `
|
||||
OFFSET ${countBy.skip.toString()}
|
||||
`;
|
||||
}
|
||||
logger.info(`${this.model.tableName} Count Statement`);
|
||||
logger.info(statement);
|
||||
|
||||
return statement;
|
||||
}
|
||||
|
||||
public toFindStatement(findBy: FindBy<TBaseModel>): {
|
||||
statement: string;
|
||||
columns: Array<string>;
|
||||
@ -247,8 +318,8 @@ export default class AnalyticsDatabaseService<
|
||||
Object.keys(findBy.query).length > 0 ? 'WHERE' : ''
|
||||
} ${this.statementGenerator.toWhereStatement(findBy.query)}
|
||||
ORDER BY ${this.statementGenerator.toSortStatemennt(findBy.sort!)}
|
||||
LIMIT ${findBy.limit}
|
||||
OFFSET ${findBy.skip}
|
||||
LIMIT ${findBy.limit.toString()}
|
||||
OFFSET ${findBy.skip.toString()}
|
||||
`;
|
||||
|
||||
logger.info(`${this.model.tableName} Find Statement`);
|
||||
@ -262,7 +333,7 @@ export default class AnalyticsDatabaseService<
|
||||
this.useDefaultDatabase();
|
||||
}
|
||||
|
||||
const statement: string = `ALTER TABLE ${
|
||||
let statement: string = `ALTER TABLE ${
|
||||
this.database.getDatasourceOptions().database
|
||||
}.${this.model.tableName}
|
||||
DELETE ${
|
||||
@ -270,6 +341,18 @@ export default class AnalyticsDatabaseService<
|
||||
} ${this.statementGenerator.toWhereStatement(deleteBy.query)}
|
||||
`;
|
||||
|
||||
if (deleteBy.limit) {
|
||||
statement += `
|
||||
LIMIT ${deleteBy.limit.toString()}
|
||||
`;
|
||||
}
|
||||
|
||||
if (deleteBy.skip) {
|
||||
statement += `
|
||||
OFFSET ${deleteBy.skip.toString()}
|
||||
`;
|
||||
}
|
||||
|
||||
logger.info(`${this.model.tableName} Delete Statement`);
|
||||
logger.info(statement);
|
||||
|
||||
@ -291,6 +374,14 @@ export default class AnalyticsDatabaseService<
|
||||
return null;
|
||||
}
|
||||
|
||||
public async deleteOneBy(deleteBy: DeleteOneBy<TBaseModel>): Promise<void> {
|
||||
return await this._deleteBy({
|
||||
...deleteBy,
|
||||
limit: 1,
|
||||
skip: 0,
|
||||
});
|
||||
}
|
||||
|
||||
public async deleteBy(deleteBy: DeleteBy<TBaseModel>): Promise<void> {
|
||||
return await this._deleteBy(deleteBy);
|
||||
}
|
||||
@ -341,6 +432,15 @@ export default class AnalyticsDatabaseService<
|
||||
});
|
||||
}
|
||||
|
||||
public async updateOneBy(
|
||||
updateOneBy: UpdateOneBy<TBaseModel>
|
||||
): Promise<void> {
|
||||
return await this._updateBy({
|
||||
...updateOneBy,
|
||||
limit: 1,
|
||||
});
|
||||
}
|
||||
|
||||
public async updateBy(updateBy: UpdateBy<TBaseModel>): Promise<void> {
|
||||
await this._updateBy(updateBy);
|
||||
}
|
||||
@ -589,6 +689,42 @@ export default class AnalyticsDatabaseService<
|
||||
}
|
||||
}
|
||||
|
||||
// emit realtime events to the client.
|
||||
if (
|
||||
this.getModel().enableRealtimeEventsOn?.create &&
|
||||
this.model.getTenantColumn()
|
||||
) {
|
||||
if (Realtime.isInitialized()) {
|
||||
const promises: Array<Promise<void>> = [];
|
||||
|
||||
for (const item of items) {
|
||||
const tenantId: ObjectID | null =
|
||||
item.getTenantColumnValue();
|
||||
|
||||
if (!tenantId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
promises.push(
|
||||
Realtime.emitModelEvent({
|
||||
model: item,
|
||||
tenantId: tenantId,
|
||||
eventType: ModelEventType.Create,
|
||||
modelType: this.modelType,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
await Promise.allSettled(promises);
|
||||
} else {
|
||||
logger.warn(
|
||||
`Realtime is not initialized. Skipping emitModelEvent for ${
|
||||
this.getModel().tableName
|
||||
}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return createBy.items;
|
||||
} catch (error) {
|
||||
await this.onCreateError(error as Exception);
|
||||
|
@ -128,7 +128,7 @@ import MonitorGroupService from './MonitorGroupService';
|
||||
import MonitorGroupResourceService from './MonitorGroupResourceService';
|
||||
import MonitorGroupOwnerUserService from './MonitorGroupOwnerUserService';
|
||||
import MonitorGroupOwnerTeamService from './MonitorGroupOwnerTeamService';
|
||||
import ServiceService from './ServiceService';
|
||||
import TelemetryServiceService from './TelemetryServiceService';
|
||||
|
||||
const services: Array<BaseService> = [
|
||||
PromoCodeService,
|
||||
@ -245,7 +245,7 @@ const services: Array<BaseService> = [
|
||||
MonitorGroupOwnerUserService,
|
||||
MonitorGroupOwnerTeamService,
|
||||
|
||||
ServiceService,
|
||||
TelemetryServiceService,
|
||||
];
|
||||
|
||||
export const AnalyticsServices: Array<
|
||||
|
@ -37,4 +37,5 @@ export class Service extends DatabaseService<Model> {
|
||||
return Promise.resolve({ createBy, carryForward: null });
|
||||
}
|
||||
}
|
||||
|
||||
export default new Service();
|
||||
|
@ -7,4 +7,5 @@ export class LogService extends AnalyticsDatabaseService<Log> {
|
||||
super({ modelType: Log, database: clickhouseDatabase });
|
||||
}
|
||||
}
|
||||
|
||||
export default new LogService();
|
||||
|
@ -11,6 +11,7 @@ import UserNotificationEventType from 'Common/Types/UserNotification/UserNotific
|
||||
export class Service extends DatabaseService<Model> {
|
||||
public constructor(postgresDatabase?: PostgresDatabase) {
|
||||
super(Model, postgresDatabase);
|
||||
this.hardDeleteItemsOlderThanInDays('createdAt', 30);
|
||||
}
|
||||
|
||||
protected override async onBeforeCreate(
|
||||
|
@ -1,5 +1,5 @@
|
||||
import PostgresDatabase from '../Infrastructure/PostgresDatabase';
|
||||
import Model from 'Model/Models/Service';
|
||||
import Model from 'Model/Models/TelemetryService';
|
||||
import DatabaseService from './DatabaseService';
|
||||
import CreateBy from '../Types/Database/CreateBy';
|
||||
import { OnCreate } from '../Types/Database/Hooks';
|
||||
@ -13,7 +13,7 @@ export class Service extends DatabaseService<Model> {
|
||||
protected override async onBeforeCreate(
|
||||
createBy: CreateBy<Model>
|
||||
): Promise<OnCreate<Model>> {
|
||||
createBy.data.serviceToken = ObjectID.generate();
|
||||
createBy.data.telemetryServiceToken = ObjectID.generate();
|
||||
|
||||
return {
|
||||
carryForward: null,
|
@ -20,6 +20,7 @@ import OnCallDutyExecutionLogTimelineStatus from 'Common/Types/OnCallDutyPolicy/
|
||||
export class Service extends DatabaseService<Model> {
|
||||
public constructor(postgresDatabase?: PostgresDatabase) {
|
||||
super(Model, postgresDatabase);
|
||||
this.hardDeleteItemsOlderThanInDays('createdAt', 30);
|
||||
}
|
||||
|
||||
protected override async onBeforeCreate(
|
||||
|
@ -20,6 +20,7 @@ import ProjectService from '../../Services/ProjectService';
|
||||
import UserType from 'Common/Types/UserType';
|
||||
import { mockRouter } from './Helpers';
|
||||
import { UserPermission } from 'Common/Types/Permission';
|
||||
import CommonAPI from '../../API/CommonAPI';
|
||||
|
||||
jest.mock('../../Utils/Express', () => {
|
||||
return {
|
||||
@ -139,9 +140,7 @@ describe('BaseAPI', () => {
|
||||
|
||||
baseApiInstance = new BaseAPI(BaseModel, TestService);
|
||||
emptyDatabaseCommonInteractionProps =
|
||||
await baseApiInstance.getDatabaseCommonInteractionProps(
|
||||
emptyRequest
|
||||
);
|
||||
await CommonAPI.getDatabaseCommonInteractionProps(emptyRequest);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@ -246,7 +245,7 @@ describe('BaseAPI', () => {
|
||||
describe('BaseAPI.getPermissionsForTenant', () => {
|
||||
it('should return empty permissions if userTenantAccessPermission is not set', async () => {
|
||||
jest.spyOn(
|
||||
baseApiInstance,
|
||||
CommonAPI,
|
||||
'getDatabaseCommonInteractionProps'
|
||||
).mockResolvedValueOnce({});
|
||||
const permissions: UserPermission[] =
|
||||
@ -258,7 +257,7 @@ describe('BaseAPI', () => {
|
||||
// eslint-disable-next-line @typescript-eslint/typedef
|
||||
const mockPermissions = [{ permission: 'granted' }];
|
||||
jest.spyOn(
|
||||
baseApiInstance,
|
||||
CommonAPI,
|
||||
'getDatabaseCommonInteractionProps'
|
||||
).mockResolvedValueOnce({
|
||||
userTenantAccessPermission: {
|
||||
@ -274,7 +273,7 @@ describe('BaseAPI', () => {
|
||||
|
||||
it('should return empty permissions if tenantId is not available', async () => {
|
||||
jest.spyOn(
|
||||
baseApiInstance,
|
||||
CommonAPI,
|
||||
'getDatabaseCommonInteractionProps'
|
||||
).mockResolvedValueOnce({
|
||||
userTenantAccessPermission: {
|
||||
@ -321,9 +320,7 @@ describe('BaseAPI', () => {
|
||||
|
||||
it('should initialize props with undefined values', async () => {
|
||||
const props: DatabaseCommonInteractionProps =
|
||||
await baseApiInstance.getDatabaseCommonInteractionProps(
|
||||
request
|
||||
);
|
||||
await CommonAPI.getDatabaseCommonInteractionProps(request);
|
||||
expect(props).toEqual(
|
||||
expect.objectContaining({
|
||||
tenantId: undefined,
|
||||
@ -339,45 +336,35 @@ describe('BaseAPI', () => {
|
||||
it('should set userId if userAuthorization is present', async () => {
|
||||
request.userAuthorization = { userId: new ObjectID('123') } as any;
|
||||
const props: DatabaseCommonInteractionProps =
|
||||
await baseApiInstance.getDatabaseCommonInteractionProps(
|
||||
request
|
||||
);
|
||||
await CommonAPI.getDatabaseCommonInteractionProps(request);
|
||||
expect(props.userId).toEqual(new ObjectID('123'));
|
||||
});
|
||||
|
||||
it('should set userGlobalAccessPermission if present in the request', async () => {
|
||||
request.userGlobalAccessPermission = { canEdit: true } as any;
|
||||
const props: DatabaseCommonInteractionProps =
|
||||
await baseApiInstance.getDatabaseCommonInteractionProps(
|
||||
request
|
||||
);
|
||||
await CommonAPI.getDatabaseCommonInteractionProps(request);
|
||||
expect(props.userGlobalAccessPermission).toEqual({ canEdit: true });
|
||||
});
|
||||
|
||||
it('should set userTenantAccessPermission if present in the request', async () => {
|
||||
request.userTenantAccessPermission = { canView: true } as any;
|
||||
const props: DatabaseCommonInteractionProps =
|
||||
await baseApiInstance.getDatabaseCommonInteractionProps(
|
||||
request
|
||||
);
|
||||
await CommonAPI.getDatabaseCommonInteractionProps(request);
|
||||
expect(props.userTenantAccessPermission).toEqual({ canView: true });
|
||||
});
|
||||
|
||||
it('should set tenantId if present in the request', async () => {
|
||||
request.tenantId = new ObjectID('456');
|
||||
const props: DatabaseCommonInteractionProps =
|
||||
await baseApiInstance.getDatabaseCommonInteractionProps(
|
||||
request
|
||||
);
|
||||
await CommonAPI.getDatabaseCommonInteractionProps(request);
|
||||
expect(props.tenantId).toEqual(new ObjectID('456'));
|
||||
});
|
||||
|
||||
it('should set isMultiTenantRequest based on headers', async () => {
|
||||
request.headers['is-multi-tenant-query'] = 'true';
|
||||
const props: DatabaseCommonInteractionProps =
|
||||
await baseApiInstance.getDatabaseCommonInteractionProps(
|
||||
request
|
||||
);
|
||||
await CommonAPI.getDatabaseCommonInteractionProps(request);
|
||||
expect(props.isMultiTenantRequest).toBe(true);
|
||||
});
|
||||
|
||||
@ -394,18 +381,14 @@ describe('BaseAPI', () => {
|
||||
);
|
||||
|
||||
const props: DatabaseCommonInteractionProps =
|
||||
await baseApiInstance.getDatabaseCommonInteractionProps(
|
||||
request
|
||||
);
|
||||
await CommonAPI.getDatabaseCommonInteractionProps(request);
|
||||
expect(props.currentPlan).toBe('Free');
|
||||
expect(props.isSubscriptionUnpaid).toBe(false);
|
||||
});
|
||||
|
||||
it('should set currentPlan and isSubscriptionUnpaid to undefined if tenantId is not present', async () => {
|
||||
const props: DatabaseCommonInteractionProps =
|
||||
await baseApiInstance.getDatabaseCommonInteractionProps(
|
||||
request
|
||||
);
|
||||
await CommonAPI.getDatabaseCommonInteractionProps(request);
|
||||
expect(props.currentPlan).toBeUndefined();
|
||||
expect(props.isSubscriptionUnpaid).toBeUndefined();
|
||||
});
|
||||
@ -414,9 +397,7 @@ describe('BaseAPI', () => {
|
||||
it('should set isMasterAdmin if userType is MasterAdmin', async () => {
|
||||
request.userType = UserType.MasterAdmin;
|
||||
const props: DatabaseCommonInteractionProps =
|
||||
await baseApiInstance.getDatabaseCommonInteractionProps(
|
||||
request
|
||||
);
|
||||
await CommonAPI.getDatabaseCommonInteractionProps(request);
|
||||
expect(props.isMasterAdmin).toBe(true);
|
||||
});
|
||||
});
|
||||
|
11
CommonServer/Types/AnalyticsDatabase/CountBy.ts
Normal file
11
CommonServer/Types/AnalyticsDatabase/CountBy.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import AnalyticsBaseModel from 'Common/AnalyticsModels/BaseModel';
|
||||
import PositiveNumber from 'Common/Types/PositiveNumber';
|
||||
import DatabaseCommonInteractionProps from 'Common/Types/BaseDatabase/DatabaseCommonInteractionProps';
|
||||
import Query from './Query';
|
||||
|
||||
export default interface CountBy<TBaseModel extends AnalyticsBaseModel> {
|
||||
query: Query<TBaseModel>;
|
||||
skip?: PositiveNumber | number;
|
||||
limit?: PositiveNumber | number;
|
||||
props: DatabaseCommonInteractionProps;
|
||||
}
|
@ -1,8 +1,9 @@
|
||||
import Query from './Query';
|
||||
import BaseModel from 'Common/AnalyticsModels/BaseModel';
|
||||
import DatabaseCommonInteractionProps from 'Common/Types/BaseDatabase/DatabaseCommonInteractionProps';
|
||||
import PositiveNumber from 'Common/Types/PositiveNumber';
|
||||
import DeleteOneBy from './DeleteOneBy';
|
||||
|
||||
export default interface DeleteOneBy<TBaseModel extends BaseModel> {
|
||||
query: Query<TBaseModel>;
|
||||
props: DatabaseCommonInteractionProps;
|
||||
export default interface DeleteBy<TBaseModel extends BaseModel>
|
||||
extends DeleteOneBy<TBaseModel> {
|
||||
limit?: PositiveNumber | number | undefined;
|
||||
skip?: PositiveNumber | number | undefined;
|
||||
}
|
||||
|
8
CommonServer/Types/AnalyticsDatabase/DeleteOneBy.ts
Normal file
8
CommonServer/Types/AnalyticsDatabase/DeleteOneBy.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import Query from './Query';
|
||||
import BaseModel from 'Common/AnalyticsModels/BaseModel';
|
||||
import DatabaseCommonInteractionProps from 'Common/Types/BaseDatabase/DatabaseCommonInteractionProps';
|
||||
|
||||
export default interface DeleteOneBy<TBaseModel extends BaseModel> {
|
||||
query: Query<TBaseModel>;
|
||||
props: DatabaseCommonInteractionProps;
|
||||
}
|
@ -1,9 +1,8 @@
|
||||
import BaseModel from 'Common/AnalyticsModels/BaseModel';
|
||||
import DatabaseCommonInteractionProps from 'Common/Types/BaseDatabase/DatabaseCommonInteractionProps';
|
||||
import Query from './Query';
|
||||
import UpdateOneBy from './UpdateOneBy';
|
||||
import PositiveNumber from 'Common/Types/PositiveNumber';
|
||||
|
||||
export default interface UpdateBy<TBaseModel extends BaseModel> {
|
||||
query: Query<TBaseModel>;
|
||||
data: TBaseModel;
|
||||
props: DatabaseCommonInteractionProps;
|
||||
export default interface UpdateBy<TBaseModel extends BaseModel>
|
||||
extends UpdateOneBy<TBaseModel> {
|
||||
limit?: PositiveNumber | number | undefined;
|
||||
}
|
||||
|
9
CommonServer/Types/AnalyticsDatabase/UpdateOneBy.ts
Normal file
9
CommonServer/Types/AnalyticsDatabase/UpdateOneBy.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import BaseModel from 'Common/AnalyticsModels/BaseModel';
|
||||
import DatabaseCommonInteractionProps from 'Common/Types/BaseDatabase/DatabaseCommonInteractionProps';
|
||||
import Query from './Query';
|
||||
|
||||
export default interface UpdateOneBy<TBaseModel extends BaseModel> {
|
||||
query: Query<TBaseModel>;
|
||||
data: TBaseModel;
|
||||
props: DatabaseCommonInteractionProps;
|
||||
}
|
@ -103,7 +103,7 @@ export default class CreateManyBaseModel<
|
||||
|
||||
array.push(
|
||||
(await this.modelService.create({
|
||||
data: JSONFunctions.fromJSON<TBaseModel>(
|
||||
data: BaseModel.fromJSON<TBaseModel>(
|
||||
(item as JSONObject) || {},
|
||||
this.modelService.modelType
|
||||
) as TBaseModel,
|
||||
@ -118,7 +118,7 @@ export default class CreateManyBaseModel<
|
||||
|
||||
return {
|
||||
returnValues: {
|
||||
models: JSONFunctions.toJSONArray(
|
||||
models: BaseModel.toJSONArray(
|
||||
array,
|
||||
this.modelService.modelType
|
||||
),
|
||||
|
@ -97,7 +97,7 @@ export default class CreateOneBaseModel<
|
||||
}
|
||||
|
||||
const model: TBaseModel = (await this.modelService.create({
|
||||
data: JSONFunctions.fromJSON<TBaseModel>(
|
||||
data: BaseModel.fromJSON<TBaseModel>(
|
||||
(args['json'] as JSONObject) || {},
|
||||
this.modelService.modelType
|
||||
) as TBaseModel,
|
||||
@ -109,10 +109,7 @@ export default class CreateOneBaseModel<
|
||||
|
||||
return {
|
||||
returnValues: {
|
||||
model: JSONFunctions.toJSON(
|
||||
model,
|
||||
this.modelService.modelType
|
||||
),
|
||||
model: BaseModel.toJSON(model, this.modelService.modelType),
|
||||
},
|
||||
executePort: successPort,
|
||||
};
|
||||
|
@ -172,7 +172,7 @@ export default class FindManyBaseModel<
|
||||
|
||||
return {
|
||||
returnValues: {
|
||||
models: JSONFunctions.toJSONArray(
|
||||
models: BaseModel.toJSONArray(
|
||||
models,
|
||||
this.modelService.modelType
|
||||
),
|
||||
|
@ -144,10 +144,7 @@ export default class FindOneBaseModel<
|
||||
return {
|
||||
returnValues: {
|
||||
model: model
|
||||
? JSONFunctions.toJSON(
|
||||
model,
|
||||
this.modelService.modelType
|
||||
)
|
||||
? BaseModel.toJSON(model, this.modelService.modelType)
|
||||
: null,
|
||||
},
|
||||
executePort: successPort,
|
||||
|
@ -98,10 +98,7 @@ export default class OnTriggerBaseModel<
|
||||
return {
|
||||
returnValues: {
|
||||
model: data
|
||||
? JSONFunctions.toJSON(
|
||||
data as any,
|
||||
this.service!.modelType
|
||||
)
|
||||
? BaseModel.toJSON(data as any, this.service!.modelType)
|
||||
: null,
|
||||
},
|
||||
executePort: successPort,
|
||||
@ -136,10 +133,7 @@ export default class OnTriggerBaseModel<
|
||||
return {
|
||||
returnValues: {
|
||||
model: data
|
||||
? JSONFunctions.toJSON(
|
||||
model as any,
|
||||
this.service!.modelType
|
||||
)
|
||||
? BaseModel.toJSON(model as any, this.service!.modelType)
|
||||
: null,
|
||||
},
|
||||
executePort: successPort,
|
||||
|
@ -29,7 +29,7 @@ export default class StatementGenerator<TBaseModel extends AnalyticsBaseModel> {
|
||||
}
|
||||
|
||||
public toUpdateStatement(updateBy: UpdateBy<TBaseModel>): string {
|
||||
const statement: string = `ALTER TABLE ${
|
||||
let statement: string = `ALTER TABLE ${
|
||||
this.database.getDatasourceOptions().database
|
||||
}.${this.model.tableName}
|
||||
UPDATE ${this.toSetStatement(updateBy.data)}
|
||||
@ -38,6 +38,10 @@ export default class StatementGenerator<TBaseModel extends AnalyticsBaseModel> {
|
||||
} ${this.toWhereStatement(updateBy.query)}
|
||||
`;
|
||||
|
||||
if (updateBy.limit) {
|
||||
statement += ` LIMIT ${updateBy.limit}`;
|
||||
}
|
||||
|
||||
logger.info(`${this.model.tableName} Update Statement`);
|
||||
logger.info(statement);
|
||||
|
||||
@ -284,7 +288,7 @@ export default class StatementGenerator<TBaseModel extends AnalyticsBaseModel> {
|
||||
throw new BadDataException(`Unknown column: ${key}`);
|
||||
}
|
||||
|
||||
whereStatement += `${key} = ${this.sanitizeValue(
|
||||
whereStatement += ` ${key} = ${this.sanitizeValue(
|
||||
value,
|
||||
tableColumn
|
||||
)} AND`;
|
||||
|
@ -11,6 +11,7 @@ import {
|
||||
import UserType from 'Common/Types/UserType';
|
||||
import Dictionary from 'Common/Types/Dictionary';
|
||||
import Port from 'Common/Types/Port';
|
||||
import { Server, createServer } from 'http';
|
||||
|
||||
export type RequestHandler = express.RequestHandler;
|
||||
export type NextFunction = express.NextFunction;
|
||||
@ -47,6 +48,7 @@ export interface OneUptimeResponse extends express.Response {
|
||||
|
||||
class Express {
|
||||
private static app: express.Application;
|
||||
private static httpServer: Server;
|
||||
|
||||
public static getRouter(): express.Router {
|
||||
return express.Router();
|
||||
@ -56,6 +58,10 @@ class Express {
|
||||
this.app = express();
|
||||
}
|
||||
|
||||
public static getHttpServer(): Server {
|
||||
return this.httpServer;
|
||||
}
|
||||
|
||||
public static getExpressApp(): express.Application {
|
||||
if (!this.app) {
|
||||
this.setupExpress();
|
||||
@ -72,12 +78,19 @@ class Express {
|
||||
this.setupExpress();
|
||||
}
|
||||
|
||||
if (!this.httpServer) {
|
||||
this.httpServer = createServer(this.app);
|
||||
}
|
||||
|
||||
return new Promise<express.Application>((resolve: Function) => {
|
||||
this.app.listen(port?.toNumber() || this.app.get('port'), () => {
|
||||
this.httpServer.listen(
|
||||
port?.toNumber() || this.app.get('port'),
|
||||
() => {
|
||||
// eslint-disable-next-line
|
||||
logger.info(`${appName} server started on port: ${port?.toNumber() || this.app.get('port')}`);
|
||||
return resolve(this.app);
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
153
CommonServer/Utils/Realtime.ts
Normal file
153
CommonServer/Utils/Realtime.ts
Normal file
@ -0,0 +1,153 @@
|
||||
import { JSONObject } from 'Common/Types/JSON';
|
||||
import IO, { SocketServer, Socket } from '../Infrastructure/SocketIO';
|
||||
import RealtimeUtil, {
|
||||
EventName,
|
||||
ListenToModelEventJSON,
|
||||
ModelEventType,
|
||||
} from 'Common/Utils/Realtime';
|
||||
import DatabaseType from 'Common/Types/BaseDatabase/DatabaseType';
|
||||
import JSONFunctions from 'Common/Types/JSONFunctions';
|
||||
import BadDataException from 'Common/Types/Exception/BadDataException';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
import BaseModel from 'Common/Models/BaseModel';
|
||||
import AnalyticsBaseModel from 'Common/AnalyticsModels/BaseModel';
|
||||
import logger from './Logger';
|
||||
|
||||
export default abstract class Realtime {
|
||||
private static socketServer: SocketServer | null = null;
|
||||
|
||||
public static isInitialized(): boolean {
|
||||
return this.socketServer !== null;
|
||||
}
|
||||
|
||||
public static init(): SocketServer | null {
|
||||
if (!this.socketServer) {
|
||||
this.socketServer = IO.getSocketServer();
|
||||
logger.info('Realtime socket server initialized');
|
||||
}
|
||||
|
||||
this.socketServer!.on('connection', (socket: Socket) => {
|
||||
socket.on(
|
||||
EventName.ListenToModalEvent,
|
||||
async (data: JSONObject) => {
|
||||
// TODO: validate if this soocket has access to this tenant
|
||||
|
||||
// TODO: validate if this socket has access to this model
|
||||
|
||||
// TODO: validate if this socket has access to this event type
|
||||
|
||||
// TODO: validate if this socket has access to this query
|
||||
|
||||
// TODO: validate if this socket has access to this select
|
||||
|
||||
// validate data
|
||||
|
||||
if (typeof data['eventType'] !== 'string') {
|
||||
throw new BadDataException('eventType is not a string');
|
||||
}
|
||||
if (typeof data['modelType'] !== 'string') {
|
||||
throw new BadDataException('modelType is not a string');
|
||||
}
|
||||
if (typeof data['modelName'] !== 'string') {
|
||||
throw new BadDataException('modelName is not a string');
|
||||
}
|
||||
if (typeof data['query'] !== 'object') {
|
||||
throw new BadDataException('query is not an object');
|
||||
}
|
||||
if (typeof data['tenantId'] !== 'string') {
|
||||
throw new BadDataException('tenantId is not a string');
|
||||
}
|
||||
if (typeof data['select'] !== 'object') {
|
||||
throw new BadDataException('select is not an object');
|
||||
}
|
||||
|
||||
await Realtime.listenToModelEvent(socket, {
|
||||
eventType: data['eventType'] as ModelEventType,
|
||||
modelType: data['modelType'] as DatabaseType,
|
||||
modelName: data['modelName'] as string,
|
||||
query: JSONFunctions.deserialize(
|
||||
data['query'] as JSONObject
|
||||
),
|
||||
tenantId: data['tenantId'] as string,
|
||||
select: JSONFunctions.deserialize(
|
||||
data['select'] as JSONObject
|
||||
),
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
return this.socketServer;
|
||||
}
|
||||
|
||||
public static async listenToModelEvent(
|
||||
socket: Socket,
|
||||
data: ListenToModelEventJSON
|
||||
): Promise<void> {
|
||||
if (!this.socketServer) {
|
||||
this.init();
|
||||
}
|
||||
|
||||
const roomId: string = RealtimeUtil.getRoomId(
|
||||
data.tenantId,
|
||||
data.modelName,
|
||||
data.eventType
|
||||
);
|
||||
|
||||
// join the room.
|
||||
await socket.join(roomId);
|
||||
}
|
||||
|
||||
public static async stopListeningToModelEvent(
|
||||
socket: Socket,
|
||||
data: ListenToModelEventJSON
|
||||
): Promise<void> {
|
||||
if (!this.socketServer) {
|
||||
this.init();
|
||||
}
|
||||
|
||||
// leave this room.
|
||||
await socket.leave(
|
||||
RealtimeUtil.getRoomId(
|
||||
data.tenantId,
|
||||
data.modelName,
|
||||
data.eventType
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public static async emitModelEvent(data: {
|
||||
tenantId: string | ObjectID;
|
||||
eventType: ModelEventType;
|
||||
model: BaseModel | AnalyticsBaseModel;
|
||||
modelType: { new (): BaseModel | AnalyticsBaseModel };
|
||||
}): Promise<void> {
|
||||
if (!this.socketServer) {
|
||||
this.init();
|
||||
}
|
||||
|
||||
let jsonObject: JSONObject = {};
|
||||
|
||||
if (data.model instanceof BaseModel) {
|
||||
jsonObject = BaseModel.toJSON(
|
||||
data.model,
|
||||
data.modelType as { new (): BaseModel }
|
||||
);
|
||||
}
|
||||
|
||||
if (data.model instanceof AnalyticsBaseModel) {
|
||||
jsonObject = AnalyticsBaseModel.toJSON(
|
||||
data.model,
|
||||
data.modelType as { new (): AnalyticsBaseModel }
|
||||
);
|
||||
}
|
||||
|
||||
const roomId: string = RealtimeUtil.getRoomId(
|
||||
data.tenantId,
|
||||
data.model.tableName!,
|
||||
data.eventType
|
||||
);
|
||||
|
||||
this.socketServer!.to(roomId).emit(roomId, jsonObject);
|
||||
}
|
||||
}
|
@ -13,11 +13,11 @@ import PositiveNumber from 'Common/Types/PositiveNumber';
|
||||
import URL from 'Common/Types/API/URL';
|
||||
import BaseModel from 'Common/Models/BaseModel';
|
||||
import EmptyResponse from 'Common/Types/API/EmptyResponse';
|
||||
import JSONFunctions from 'Common/Types/JSONFunctions';
|
||||
import FileModel from 'Common/Models/FileModel';
|
||||
import Dictionary from 'Common/Types/Dictionary';
|
||||
import StatusCode from 'Common/Types/API/StatusCode';
|
||||
import { DEFAULT_LIMIT } from 'Common/Types/Database/LimitMax';
|
||||
import AnalyticsDataModel from 'Common/AnalyticsModels/BaseModel';
|
||||
|
||||
export default class Response {
|
||||
private static logResponse(
|
||||
@ -174,29 +174,40 @@ export default class Response {
|
||||
public static sendEntityArrayResponse(
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse,
|
||||
list: Array<BaseModel>,
|
||||
list: Array<BaseModel | AnalyticsDataModel>,
|
||||
count: PositiveNumber | number,
|
||||
modelType: { new (): BaseModel }
|
||||
modelType: { new (): BaseModel | AnalyticsDataModel }
|
||||
): void {
|
||||
if (!(count instanceof PositiveNumber)) {
|
||||
count = new PositiveNumber(count);
|
||||
}
|
||||
|
||||
return this.sendJsonArrayResponse(
|
||||
req,
|
||||
res,
|
||||
JSONFunctions.serializeArray(
|
||||
JSONFunctions.toJSONArray(list as Array<BaseModel>, modelType)
|
||||
),
|
||||
count
|
||||
let jsonArray: JSONArray = [];
|
||||
|
||||
const model: BaseModel | AnalyticsDataModel = new modelType();
|
||||
|
||||
if (model instanceof BaseModel) {
|
||||
jsonArray = BaseModel.toJSONArray(
|
||||
list as Array<BaseModel>,
|
||||
modelType as { new (): BaseModel }
|
||||
);
|
||||
}
|
||||
|
||||
if (model instanceof AnalyticsDataModel) {
|
||||
jsonArray = AnalyticsDataModel.toJSONArray(
|
||||
list as Array<AnalyticsDataModel>,
|
||||
modelType as { new (): AnalyticsDataModel }
|
||||
);
|
||||
}
|
||||
|
||||
return this.sendJsonArrayResponse(req, res, jsonArray, count);
|
||||
}
|
||||
|
||||
public static sendEntityResponse(
|
||||
req: ExpressRequest,
|
||||
res: ExpressResponse,
|
||||
item: BaseModel | null,
|
||||
modelType: { new (): BaseModel },
|
||||
item: BaseModel | AnalyticsDataModel | null,
|
||||
modelType: { new (): BaseModel | AnalyticsDataModel },
|
||||
options?:
|
||||
| {
|
||||
miscData?: JSONObject;
|
||||
@ -205,9 +216,17 @@ export default class Response {
|
||||
): void {
|
||||
let response: JSONObject = {};
|
||||
|
||||
if (item) {
|
||||
response = JSONFunctions.serialize(
|
||||
JSONFunctions.toJSONObject(item, modelType)
|
||||
if (item && item instanceof BaseModel) {
|
||||
response = BaseModel.toJSON(
|
||||
item,
|
||||
modelType as { new (): BaseModel }
|
||||
);
|
||||
}
|
||||
|
||||
if (item && item instanceof AnalyticsDataModel) {
|
||||
response = AnalyticsDataModel.toJSON(
|
||||
item,
|
||||
modelType as { new (): AnalyticsDataModel }
|
||||
);
|
||||
}
|
||||
|
||||
|
30
CommonServer/package-lock.json
generated
30
CommonServer/package-lock.json
generated
@ -14,6 +14,7 @@
|
||||
"@opentelemetry/api": "^1.1.0",
|
||||
"@opentelemetry/auto-instrumentations-node": "^0.31.0",
|
||||
"@opentelemetry/sdk-node": "^0.30.0",
|
||||
"@socket.io/redis-adapter": "^8.2.1",
|
||||
"@types/ejs": "^3.1.1",
|
||||
"@types/gridfs-stream": "^0.5.35",
|
||||
"@types/json2csv": "^5.0.3",
|
||||
@ -3733,6 +3734,22 @@
|
||||
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz",
|
||||
"integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg=="
|
||||
},
|
||||
"node_modules/@socket.io/redis-adapter": {
|
||||
"version": "8.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@socket.io/redis-adapter/-/redis-adapter-8.2.1.tgz",
|
||||
"integrity": "sha512-6Dt7EZgGSBP0qvXeOKGx7NnSr2tPMbVDfDyL97zerZo+v69hMfL99skMCL3RKZlWVqLyRme2T0wcy3udHhtOsg==",
|
||||
"dependencies": {
|
||||
"debug": "~4.3.1",
|
||||
"notepack.io": "~3.0.1",
|
||||
"uid2": "1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"socket.io-adapter": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@sqltools/formatter": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/@sqltools/formatter/-/formatter-1.2.5.tgz",
|
||||
@ -8328,6 +8345,11 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/notepack.io": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/notepack.io/-/notepack.io-3.0.1.tgz",
|
||||
"integrity": "sha512-TKC/8zH5pXIAMVQio2TvVDTtPRX+DJPHDqjRbxogtFiByHyzKmy96RA0JtCQJ+WouyyL4A10xomQzgbUT+1jCg=="
|
||||
},
|
||||
"node_modules/npm-run-path": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
|
||||
@ -10269,6 +10291,14 @@
|
||||
"node": ">=0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/uid2": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/uid2/-/uid2-1.0.0.tgz",
|
||||
"integrity": "sha512-+I6aJUv63YAcY9n4mQreLUt0d4lvwkkopDNmpomkAUz0fAkEMV9pRWxN0EjhW1YfRhcuyHg2v3mwddCDW1+LFQ==",
|
||||
"engines": {
|
||||
"node": ">= 4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/undici": {
|
||||
"version": "5.27.2",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-5.27.2.tgz",
|
||||
|
@ -17,6 +17,7 @@
|
||||
"@opentelemetry/api": "^1.1.0",
|
||||
"@opentelemetry/auto-instrumentations-node": "^0.31.0",
|
||||
"@opentelemetry/sdk-node": "^0.30.0",
|
||||
"@socket.io/redis-adapter": "^8.2.1",
|
||||
"@types/ejs": "^3.1.1",
|
||||
"@types/gridfs-stream": "^0.5.35",
|
||||
"@types/json2csv": "^5.0.3",
|
||||
@ -43,7 +44,7 @@
|
||||
"nodemailer": "^6.7.3",
|
||||
"nodemailer-express-handlebars": "^5.0.0",
|
||||
"pg": "^8.7.3",
|
||||
"socket.io": "^4.4.1",
|
||||
"socket.io": "^4.7.2",
|
||||
"stripe": "^10.17.0",
|
||||
"twilio": "^4.13.0",
|
||||
"typeorm": "^0.3.10",
|
||||
|
109
CommonUI/package-lock.json
generated
109
CommonUI/package-lock.json
generated
@ -57,6 +57,7 @@
|
||||
"redux": "^4.2.0",
|
||||
"rehype-sanitize": "^5.0.1",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"socket.io-client": "^4.7.2",
|
||||
"tailwindcss": "^3.2.4",
|
||||
"tippy.js": "^6.3.7",
|
||||
"universal-cookie": "^4.0.4",
|
||||
@ -1885,6 +1886,11 @@
|
||||
"@sinonjs/commons": "^1.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@socket.io/component-emitter": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz",
|
||||
"integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg=="
|
||||
},
|
||||
"node_modules/@testing-library/dom": {
|
||||
"version": "8.19.0",
|
||||
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.19.0.tgz",
|
||||
@ -3867,6 +3873,26 @@
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/engine.io-client": {
|
||||
"version": "6.5.3",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.3.tgz",
|
||||
"integrity": "sha512-9Z0qLB0NIisTRt1DZ/8U2k12RJn8yls/nXMZLn+/N8hANT3TcYjKFKcwbw5zFQiN4NTde3TSY9zb79e1ij6j9Q==",
|
||||
"dependencies": {
|
||||
"@socket.io/component-emitter": "~3.1.0",
|
||||
"debug": "~4.3.1",
|
||||
"engine.io-parser": "~5.2.1",
|
||||
"ws": "~8.11.0",
|
||||
"xmlhttprequest-ssl": "~2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/engine.io-parser": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.1.tgz",
|
||||
"integrity": "sha512-9JktcM3u18nU9N2Lz3bWeBgxVgOKpw7yhRaoxQA3FUDZzzw+9WlA6p4G4u0RixNkg14fH7EfEc/RhpurtiROTQ==",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/error-ex": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
|
||||
@ -11136,6 +11162,32 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io-client": {
|
||||
"version": "4.7.2",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.2.tgz",
|
||||
"integrity": "sha512-vtA0uD4ibrYD793SOIAwlo8cj6haOeMHrGvwPxJsxH7CeIksqJ+3Zc06RvWTIFgiSqx4A3sOnTXpfAEE2Zyz6w==",
|
||||
"dependencies": {
|
||||
"@socket.io/component-emitter": "~3.1.0",
|
||||
"debug": "~4.3.2",
|
||||
"engine.io-client": "~6.5.2",
|
||||
"socket.io-parser": "~4.2.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io-parser": {
|
||||
"version": "4.2.4",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
|
||||
"integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==",
|
||||
"dependencies": {
|
||||
"@socket.io/component-emitter": "~3.1.0",
|
||||
"debug": "~4.3.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
@ -12129,7 +12181,6 @@
|
||||
"version": "8.11.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz",
|
||||
"integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
@ -12161,6 +12212,14 @@
|
||||
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/xmlhttprequest-ssl": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz",
|
||||
"integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/xtend": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||
@ -13631,6 +13690,11 @@
|
||||
"@sinonjs/commons": "^1.7.0"
|
||||
}
|
||||
},
|
||||
"@socket.io/component-emitter": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz",
|
||||
"integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg=="
|
||||
},
|
||||
"@testing-library/dom": {
|
||||
"version": "8.19.0",
|
||||
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.19.0.tgz",
|
||||
@ -15260,6 +15324,23 @@
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
||||
"dev": true
|
||||
},
|
||||
"engine.io-client": {
|
||||
"version": "6.5.3",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.3.tgz",
|
||||
"integrity": "sha512-9Z0qLB0NIisTRt1DZ/8U2k12RJn8yls/nXMZLn+/N8hANT3TcYjKFKcwbw5zFQiN4NTde3TSY9zb79e1ij6j9Q==",
|
||||
"requires": {
|
||||
"@socket.io/component-emitter": "~3.1.0",
|
||||
"debug": "~4.3.1",
|
||||
"engine.io-parser": "~5.2.1",
|
||||
"ws": "~8.11.0",
|
||||
"xmlhttprequest-ssl": "~2.0.0"
|
||||
}
|
||||
},
|
||||
"engine.io-parser": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.1.tgz",
|
||||
"integrity": "sha512-9JktcM3u18nU9N2Lz3bWeBgxVgOKpw7yhRaoxQA3FUDZzzw+9WlA6p4G4u0RixNkg14fH7EfEc/RhpurtiROTQ=="
|
||||
},
|
||||
"error-ex": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
|
||||
@ -20338,6 +20419,26 @@
|
||||
"integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
|
||||
"dev": true
|
||||
},
|
||||
"socket.io-client": {
|
||||
"version": "4.7.2",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.2.tgz",
|
||||
"integrity": "sha512-vtA0uD4ibrYD793SOIAwlo8cj6haOeMHrGvwPxJsxH7CeIksqJ+3Zc06RvWTIFgiSqx4A3sOnTXpfAEE2Zyz6w==",
|
||||
"requires": {
|
||||
"@socket.io/component-emitter": "~3.1.0",
|
||||
"debug": "~4.3.2",
|
||||
"engine.io-client": "~6.5.2",
|
||||
"socket.io-parser": "~4.2.4"
|
||||
}
|
||||
},
|
||||
"socket.io-parser": {
|
||||
"version": "4.2.4",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
|
||||
"integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==",
|
||||
"requires": {
|
||||
"@socket.io/component-emitter": "~3.1.0",
|
||||
"debug": "~4.3.1"
|
||||
}
|
||||
},
|
||||
"source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
@ -21049,7 +21150,6 @@
|
||||
"version": "8.11.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz",
|
||||
"integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"xml-name-validator": {
|
||||
@ -21064,6 +21164,11 @@
|
||||
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
|
||||
"dev": true
|
||||
},
|
||||
"xmlhttprequest-ssl": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz",
|
||||
"integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A=="
|
||||
},
|
||||
"xtend": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||
|
@ -59,6 +59,7 @@
|
||||
"redux": "^4.2.0",
|
||||
"rehype-sanitize": "^5.0.1",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"socket.io-client": "^4.7.2",
|
||||
"tailwindcss": "^3.2.4",
|
||||
"tippy.js": "^6.3.7",
|
||||
"universal-cookie": "^4.0.4",
|
||||
|
@ -23,6 +23,7 @@ export interface ComponentProps {
|
||||
tabIndex?: number | undefined;
|
||||
error?: string | undefined;
|
||||
value?: string | undefined;
|
||||
showLineNumbers?: boolean | undefined;
|
||||
}
|
||||
|
||||
const CodeEditor: FunctionComponent<ComponentProps> = (
|
||||
@ -153,7 +154,7 @@ const CodeEditor: FunctionComponent<ComponentProps> = (
|
||||
scrollBeyondLastColumn: 5,
|
||||
scrollBeyondLastLine: true,
|
||||
selectOnLineNumbers: true,
|
||||
lineNumbers: 'off',
|
||||
lineNumbers: props.showLineNumbers ? 'on' :'off',
|
||||
selectionClipboard: true,
|
||||
selectionHighlight: true,
|
||||
showFoldingControls: 'mouseover',
|
||||
|
16
CommonUI/src/Components/ComingSoon/ComingSoon.tsx
Normal file
16
CommonUI/src/Components/ComingSoon/ComingSoon.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
import React, { FunctionComponent, ReactElement } from 'react';
|
||||
import EmptyState from '../EmptyState/EmptyState';
|
||||
import IconProp from 'Common/Types/Icon/IconProp';
|
||||
|
||||
const ComingSoon: FunctionComponent = (): ReactElement => {
|
||||
return (
|
||||
<EmptyState
|
||||
id="coming-soon"
|
||||
icon={IconProp.CursorArrowRays}
|
||||
title="Coming soon!"
|
||||
description="We will be launching this feature very soon. Stay Tuned!"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default ComingSoon;
|
@ -108,7 +108,7 @@ const Dropdown: FunctionComponent<ComponentProps> = (
|
||||
return (
|
||||
<div
|
||||
className={`${
|
||||
props.className || 'relative mt-2 mb-1 rounded-md w-full'
|
||||
props.className || 'relative mt-2 mb-1 rounded-md w-full overflow-visible'
|
||||
}`}
|
||||
onClick={() => {
|
||||
props.onClick && props.onClick();
|
||||
|
@ -13,7 +13,6 @@ import HTTPResponse from 'Common/Types/API/HTTPResponse';
|
||||
import Route from 'Common/Types/API/Route';
|
||||
import Navigation from '../../Utils/Navigation';
|
||||
import BasicFormModal from '../FormModal/BasicFormModal';
|
||||
import JSONFunctions from 'Common/Types/JSONFunctions';
|
||||
|
||||
export interface ComponentProps<TBaseModel extends BaseModel> {
|
||||
modelType: { new (): TBaseModel };
|
||||
@ -119,7 +118,7 @@ const DuplicateModel: <TBaseModel extends BaseModel>(
|
||||
onSubmit={(item: TBaseModel) => {
|
||||
setShowModal(false);
|
||||
duplicateItem(
|
||||
JSONFunctions.fromJSONObject(
|
||||
BaseModel.fromJSONObject(
|
||||
item,
|
||||
props.modelType
|
||||
) as TBaseModel
|
||||
|
@ -8,6 +8,7 @@ export interface ComponentProps {
|
||||
icon: IconProp | undefined;
|
||||
footer?: ReactElement | undefined;
|
||||
id: string;
|
||||
iconClassName?: string;
|
||||
}
|
||||
|
||||
const EmptyState: FunctionComponent<ComponentProps> = (
|
||||
@ -20,7 +21,10 @@ const EmptyState: FunctionComponent<ComponentProps> = (
|
||||
{props.icon && (
|
||||
<Icon
|
||||
icon={props.icon}
|
||||
className="mx-auto h-12 w-12 text-gray-400"
|
||||
className={
|
||||
props.iconClassName ||
|
||||
`mx-auto h-12 w-12 text-gray-400`
|
||||
}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
@ -30,7 +30,6 @@ import TableColumnType from 'Common/Types/Database/TableColumnType';
|
||||
import Typeof from 'Common/Types/Typeof';
|
||||
import { TableColumnMetadata } from 'Common/Types/Database/TableColumn';
|
||||
import { ButtonStyleType } from '../Button/Button';
|
||||
import JSONFunctions from 'Common/Types/JSONFunctions';
|
||||
import API from '../../Utils/API/API';
|
||||
import { FormStep } from './Types/FormStep';
|
||||
import Field from './Types/Field';
|
||||
@ -292,7 +291,7 @@ const ModelForm: <TBaseModel extends BaseModel>(
|
||||
);
|
||||
|
||||
if (!(item instanceof BaseModel) && item) {
|
||||
item = JSONFunctions.fromJSON(
|
||||
item = BaseModel.fromJSON(
|
||||
item as JSONObject,
|
||||
props.modelType
|
||||
) as BaseModel;
|
||||
@ -524,7 +523,7 @@ const ModelForm: <TBaseModel extends BaseModel>(
|
||||
}
|
||||
}
|
||||
|
||||
let tBaseModel: TBaseModel = JSONFunctions.fromJSON(
|
||||
let tBaseModel: TBaseModel = BaseModel.fromJSON(
|
||||
valuesToSend,
|
||||
props.modelType
|
||||
) as TBaseModel;
|
||||
@ -552,7 +551,7 @@ const ModelForm: <TBaseModel extends BaseModel>(
|
||||
|
||||
if (props.onSuccess) {
|
||||
props.onSuccess(
|
||||
JSONFunctions.fromJSONObject(result.data, props.modelType),
|
||||
BaseModel.fromJSONObject(result.data, props.modelType),
|
||||
miscData
|
||||
);
|
||||
}
|
||||
|
@ -141,6 +141,10 @@ const Icon: FunctionComponent<ComponentProps> = ({
|
||||
d="M3.75 12h16.5m-16.5 3.75h16.5M3.75 19.5h16.5M5.625 4.5h12.75a1.875 1.875 0 010 3.75H5.625a1.875 1.875 0 010-3.75z"
|
||||
/>
|
||||
);
|
||||
}else if (icon === IconProp.Stop) {
|
||||
return getSvgWrapper(
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M5.25 7.5A2.25 2.25 0 017.5 5.25h9a2.25 2.25 0 012.25 2.25v9a2.25 2.25 0 01-2.25 2.25h-9a2.25 2.25 0 01-2.25-2.25v-9z" />
|
||||
);
|
||||
} else if (icon === IconProp.Copy) {
|
||||
return getSvgWrapper(
|
||||
<path
|
||||
@ -165,6 +169,22 @@ const Icon: FunctionComponent<ComponentProps> = ({
|
||||
d="M6 6.878V6a2.25 2.25 0 012.25-2.25h7.5A2.25 2.25 0 0118 6v.878m-12 0c.235-.083.487-.128.75-.128h10.5c.263 0 .515.045.75.128m-12 0A2.25 2.25 0 004.5 9v.878m13.5-3A2.25 2.25 0 0119.5 9v.878m0 0a2.246 2.246 0 00-.75-.128H5.25c-.263 0-.515.045-.75.128m15 0A2.25 2.25 0 0121 12v6a2.25 2.25 0 01-2.25 2.25H5.25A2.25 2.25 0 013 18v-6c0-.98.626-1.813 1.5-2.122"
|
||||
/>
|
||||
);
|
||||
} else if (icon === IconProp.CursorArrowRays) {
|
||||
return getSvgWrapper(
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M15.042 21.672L13.684 16.6m0 0l-2.51 2.225.569-9.47 5.227 7.917-3.286-.672zM12 2.25V4.5m5.834.166l-1.591 1.591M20.25 10.5H18M7.757 14.743l-1.59 1.59M6 10.5H3.75m4.007-4.243l-1.59-1.59"
|
||||
/>
|
||||
);
|
||||
} else if (icon === IconProp.ChartBar) {
|
||||
return getSvgWrapper(
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M3 13.125C3 12.504 3.504 12 4.125 12h2.25c.621 0 1.125.504 1.125 1.125v6.75C7.5 20.496 6.996 21 6.375 21h-2.25A1.125 1.125 0 013 19.875v-6.75zM9.75 8.625c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125v11.25c0 .621-.504 1.125-1.125 1.125h-2.25a1.125 1.125 0 01-1.125-1.125V8.625zM16.5 4.125c0-.621.504-1.125 1.125-1.125h2.25C20.496 3 21 3.504 21 4.125v15.75c0 .621-.504 1.125-1.125 1.125h-2.25a1.125 1.125 0 01-1.125-1.125V4.125z"
|
||||
/>
|
||||
);
|
||||
} else if (icon === IconProp.SquareStack) {
|
||||
return getSvgWrapper(
|
||||
<path
|
||||
|
147
CommonUI/src/Components/LogsViewer/LogItem.tsx
Normal file
147
CommonUI/src/Components/LogsViewer/LogItem.tsx
Normal file
@ -0,0 +1,147 @@
|
||||
import React, { FunctionComponent, ReactElement, useEffect } from 'react';
|
||||
import Log from 'Model/AnalyticsModels/Log';
|
||||
import OneUptimeDate from 'Common/Types/Date';
|
||||
|
||||
export interface ComponentProps {
|
||||
log: Log;
|
||||
}
|
||||
|
||||
const LogItem: FunctionComponent<ComponentProps> = (
|
||||
props: ComponentProps
|
||||
): ReactElement => {
|
||||
const [isCollapsed, setIsCollapsed] = React.useState<boolean>(true);
|
||||
|
||||
useEffect(() => {
|
||||
setIsCollapsed(true);
|
||||
}, []);
|
||||
|
||||
let bodyColor: string = 'text-slate-200';
|
||||
|
||||
if (props.log.severityText === 'Warning') {
|
||||
bodyColor = 'text-amber-400';
|
||||
} else if (props.log.severityText === 'Error') {
|
||||
bodyColor = 'text-rose-400';
|
||||
}
|
||||
|
||||
if (isCollapsed) {
|
||||
return (
|
||||
<div
|
||||
className="text-slate-200 flex cursor-pointer hover:border-slate-700 px-2 border-transparent border-2 rounded-md"
|
||||
onClick={() => {
|
||||
setIsCollapsed(false);
|
||||
}}
|
||||
>
|
||||
{props.log.time && (
|
||||
<div
|
||||
className="text-slate-500 courier-prime flex-none"
|
||||
style={{
|
||||
width: '230px !important',
|
||||
}}
|
||||
>
|
||||
{OneUptimeDate.getDateAsLocalFormattedString(
|
||||
props.log.time
|
||||
)}{' '}
|
||||
{' '}
|
||||
</div>
|
||||
)}
|
||||
{props.log.severityText === 'Information' && (
|
||||
<div className="text-sky-400 courier-prime flex-none">
|
||||
[INFO]
|
||||
</div>
|
||||
)}
|
||||
{props.log.severityText === 'Warning' && (
|
||||
<div className="text-amber-400 courier-prime flex-none">
|
||||
[WARN]
|
||||
</div>
|
||||
)}
|
||||
{props.log.severityText === 'Error' && (
|
||||
<div className="text-rose-400 courier-prime flex-none">
|
||||
[EROR]
|
||||
</div>
|
||||
)}
|
||||
<div className={`${bodyColor} courier-prime`}>
|
||||
{props.log.body?.toString()}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="text-slate-200 cursor-pointer hover:border-slate-700 px-2 border-transparent border-2 rounded-md"
|
||||
onClick={() => {
|
||||
setIsCollapsed(true);
|
||||
}}
|
||||
>
|
||||
{props.log.time && (
|
||||
<div className="text-slate-500 courier-prime">
|
||||
{OneUptimeDate.getDateAsFormattedString(props.log.time)}{' '}
|
||||
{' '}
|
||||
</div>
|
||||
)}
|
||||
{props.log.severityText === 'Information' && (
|
||||
<div className="flex">
|
||||
<div className="font-medium text-slate-200 courier-prime mr-2">
|
||||
SEVERITY:
|
||||
</div>
|
||||
<div className="text-sky-400 courier-prime">
|
||||
[INFO]
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{props.log.severityText === 'Warning' && (
|
||||
<div className="flex">
|
||||
<div className="font-medium text-slate-200 courier-prime mr-2">
|
||||
SEVERITY:
|
||||
</div>
|
||||
<div className="text-amber-400 courier-prime">
|
||||
[WARN]
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{props.log.severityText === 'Error' && (
|
||||
<div className="flex">
|
||||
<div className="font-medium text-slate-200 courier-prime mr-2">
|
||||
SEVERITY:
|
||||
</div>
|
||||
<div className="text-rose-400 courier-prime">
|
||||
[EROR]
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex">
|
||||
<div className="font-medium text-slate-200 courier-prime mr-2">
|
||||
MESSAGE:
|
||||
</div>
|
||||
<div className={`${bodyColor} courier-prime`}>
|
||||
{props.log.body?.toString()}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{props.log.traceId && (
|
||||
<div className="flex">
|
||||
<div className="font-medium text-slate-200 courier-prime mr-2">
|
||||
TRACE:
|
||||
</div>
|
||||
<div className={`${bodyColor} courier-prime`}>
|
||||
{props.log.traceId?.toString()}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{props.log.spanId && (
|
||||
<div className="flex">
|
||||
<div className="font-medium text-slate-200 courier-prime mr-2">
|
||||
SPAN:
|
||||
</div>
|
||||
<div className={`${bodyColor} courier-prime`}>
|
||||
{props.log.spanId?.toString()}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LogItem;
|
218
CommonUI/src/Components/LogsViewer/LogsFilters.tsx
Normal file
218
CommonUI/src/Components/LogsViewer/LogsFilters.tsx
Normal file
@ -0,0 +1,218 @@
|
||||
import React, { FunctionComponent, ReactElement, useEffect } from 'react';
|
||||
import Input from '../Input/Input';
|
||||
import Button, { ButtonStyleType } from '../Button/Button';
|
||||
import IconProp from 'Common/Types/Icon/IconProp';
|
||||
import Dropdown from '../Dropdown/Dropdown';
|
||||
import FieldLabelElement from '../Forms/Fields/FieldLabel';
|
||||
import TelemetryService from 'Model/Models/TelemetryService';
|
||||
import CodeEditor from '../CodeEditor/CodeEditor';
|
||||
import CodeType from 'Common/Types/Code/CodeType';
|
||||
|
||||
export interface FiterOptions {
|
||||
searchText?: string | undefined;
|
||||
}
|
||||
|
||||
export interface ComponentProps {
|
||||
onFilterChanged: (filterOptions: FiterOptions) => void;
|
||||
onAutoScrollChanged: (turnOnAutoScroll: boolean) => void;
|
||||
telemetryServices?: Array<TelemetryService>
|
||||
}
|
||||
|
||||
const LogsFilters: FunctionComponent<ComponentProps> = (
|
||||
props: ComponentProps
|
||||
): ReactElement => {
|
||||
const [filterOptions, setFilterOptions] = React.useState<FiterOptions>({});
|
||||
|
||||
const [turnOnAutoScroll, setTurnOnAutoScroll] = React.useState<boolean>(true);
|
||||
const [showMoreFilters, setShowMoreFilters] = React.useState<boolean>(false);
|
||||
const [isSqlQuery, setIsSqlQuery] = React.useState<boolean>(false);
|
||||
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
props.onFilterChanged(filterOptions);
|
||||
}, [filterOptions]);
|
||||
|
||||
return (
|
||||
<div className="shadow sm:overflow-hidden sm:rounded-md">
|
||||
<div className="bg-white py-6 px-4 sm:p-6">
|
||||
<div >
|
||||
<div className='flex space-x-2 justify-between'>
|
||||
<div className='w-full mr-2'>
|
||||
{!isSqlQuery && <div className='space-y-4'>
|
||||
|
||||
<div>
|
||||
<FieldLabelElement
|
||||
title='Search Logs'
|
||||
required={true}
|
||||
/>
|
||||
<Input
|
||||
placeholder="Search"
|
||||
onChange={(value: string) => {
|
||||
setFilterOptions({
|
||||
searchText: value,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{showMoreFilters && <div>
|
||||
<FieldLabelElement
|
||||
title='Telemetry Services'
|
||||
required={true}
|
||||
/>
|
||||
<Dropdown isMultiSelect={true} options={[
|
||||
{
|
||||
label: 'Information',
|
||||
value: 'information'
|
||||
},
|
||||
{
|
||||
label: 'Warning',
|
||||
value: 'warning'
|
||||
},
|
||||
{
|
||||
label: 'Error',
|
||||
value: 'error'
|
||||
}
|
||||
]} />
|
||||
</div>}
|
||||
{showMoreFilters && <div>
|
||||
<FieldLabelElement
|
||||
title='Log Severity'
|
||||
required={true}
|
||||
/>
|
||||
<Dropdown isMultiSelect={true} options={[
|
||||
{
|
||||
label: 'Information',
|
||||
value: 'information'
|
||||
},
|
||||
{
|
||||
label: 'Warning',
|
||||
value: 'warning'
|
||||
},
|
||||
{
|
||||
label: 'Error',
|
||||
value: 'error'
|
||||
}
|
||||
]} />
|
||||
</div>}
|
||||
{showMoreFilters && <div className="flex space-x-2 w-full">
|
||||
<div className='w-1/2'>
|
||||
<FieldLabelElement
|
||||
title='Start Time'
|
||||
required={true}
|
||||
/>
|
||||
<Input
|
||||
placeholder="Start Time"
|
||||
onChange={(value: string) => {
|
||||
setFilterOptions({
|
||||
searchText: value,
|
||||
});
|
||||
}}
|
||||
type={'datetime-local'}
|
||||
/>
|
||||
</div>
|
||||
<div className='w-1/2'>
|
||||
<FieldLabelElement
|
||||
title='End Time'
|
||||
required={true}
|
||||
/>
|
||||
<Input
|
||||
placeholder="End Time"
|
||||
onChange={(value: string) => {
|
||||
setFilterOptions({
|
||||
searchText: value,
|
||||
});
|
||||
}}
|
||||
type={'datetime-local'}
|
||||
/>
|
||||
</div>
|
||||
</div>}
|
||||
|
||||
</div>}
|
||||
{isSqlQuery && <div>
|
||||
<div className='space-y-1'>
|
||||
<FieldLabelElement
|
||||
title='SQL Query'
|
||||
required={true}
|
||||
description='Enter a SQL query to filter logs.'
|
||||
/>
|
||||
<CodeEditor
|
||||
type={CodeType.SQL}
|
||||
placeholder="SQL Query"
|
||||
onChange={(value: string) => {
|
||||
setFilterOptions({
|
||||
searchText: value,
|
||||
});
|
||||
}}
|
||||
showLineNumbers={true}
|
||||
/>
|
||||
</div>
|
||||
</div>}
|
||||
</div>
|
||||
{!isSqlQuery && <div>
|
||||
<div className='mt-7 -ml-5 justify-end flex w-44'>
|
||||
{!turnOnAutoScroll && <Button title='Start Autoscroll' icon={IconProp.Play} onClick={() => {
|
||||
setTurnOnAutoScroll(true);
|
||||
props.onAutoScrollChanged(true);
|
||||
}} />}
|
||||
{turnOnAutoScroll && <Button title='Stop Autoscroll' icon={IconProp.Stop} onClick={() => {
|
||||
setTurnOnAutoScroll(false);
|
||||
props.onAutoScrollChanged(false);
|
||||
}} />}
|
||||
</div>
|
||||
|
||||
</div>}
|
||||
{isSqlQuery && <div className=''>
|
||||
<div className='mt-12 -ml-8 justify-end flex w-44'>
|
||||
<Button title='Search with SQL' onClick={() => {
|
||||
|
||||
}} />
|
||||
</div>
|
||||
|
||||
</div>}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div className='flex justify-between -ml-2 -mr-2'>
|
||||
<div className='flex'>
|
||||
{!isSqlQuery && <div>
|
||||
{!showMoreFilters && <Button buttonStyle={ButtonStyleType.SECONDARY_LINK} title='Show More Options' onClick={() => {
|
||||
setShowMoreFilters(true);
|
||||
}} />}
|
||||
{showMoreFilters && <Button buttonStyle={ButtonStyleType.SECONDARY_LINK} title='Hide More Options' onClick={() => {
|
||||
setShowMoreFilters(false);
|
||||
}} />}
|
||||
</div>}
|
||||
<div>
|
||||
{!isSqlQuery && <Button buttonStyle={ButtonStyleType.SECONDARY_LINK} title='Search with SQL instead' onClick={() => {
|
||||
setIsSqlQuery(true);
|
||||
}} />}
|
||||
{isSqlQuery && <Button buttonStyle={ButtonStyleType.SECONDARY_LINK} title='Search with Text instead' onClick={() => {
|
||||
setIsSqlQuery(false);
|
||||
}} />}
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex'>
|
||||
<div>
|
||||
<Button buttonStyle={ButtonStyleType.SECONDARY_LINK} title='Save as Preset' onClick={() => {
|
||||
setIsSqlQuery(true);
|
||||
}} />
|
||||
</div>
|
||||
<div>
|
||||
<Button buttonStyle={ButtonStyleType.SECONDARY_LINK} title='Load from Preset' onClick={() => {
|
||||
setIsSqlQuery(true);
|
||||
}} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LogsFilters;
|
89
CommonUI/src/Components/LogsViewer/LogsViewer.tsx
Normal file
89
CommonUI/src/Components/LogsViewer/LogsViewer.tsx
Normal file
@ -0,0 +1,89 @@
|
||||
import Log from 'Model/AnalyticsModels/Log';
|
||||
import React, { FunctionComponent, ReactElement, Ref } from 'react';
|
||||
import LogItem from './LogItem';
|
||||
import LogsFilters, { FiterOptions } from './LogsFilters';
|
||||
|
||||
export interface ComponentProps {
|
||||
logs: Array<Log>;
|
||||
onFilterChanged: (filterOptions: FiterOptions) => void;
|
||||
|
||||
}
|
||||
|
||||
const LogsViewer: FunctionComponent<ComponentProps> = (
|
||||
props: ComponentProps
|
||||
): ReactElement => {
|
||||
const [screenHeight, setScreenHeight] = React.useState<number>(
|
||||
window.innerHeight
|
||||
);
|
||||
const [autoScroll, setAutoScroll] = React.useState<boolean>(true);
|
||||
const logsViewerRef: Ref<HTMLDivElement> =
|
||||
React.useRef<HTMLDivElement>(null);
|
||||
|
||||
// Update the screen height when the window is resized
|
||||
|
||||
React.useEffect(() => {
|
||||
const handleResize: any = (): void => {
|
||||
setScreenHeight(window.innerHeight);
|
||||
};
|
||||
|
||||
window.addEventListener('resize', handleResize);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('resize', handleResize);
|
||||
};
|
||||
}, []);
|
||||
|
||||
|
||||
// Keep scroll to the bottom of the log
|
||||
|
||||
const scrollToBottom = (): void => {
|
||||
const logsViewer: HTMLDivElement | null = logsViewerRef.current;
|
||||
|
||||
if (logsViewer) {
|
||||
logsViewer.scrollTop = logsViewer.scrollHeight;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!autoScroll) {
|
||||
return;
|
||||
}
|
||||
|
||||
scrollToBottom();
|
||||
}, [props.logs]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className='mb-5'>
|
||||
<LogsFilters onAutoScrollChanged={(autoscroll: boolean)=>{
|
||||
setAutoScroll(autoscroll);
|
||||
|
||||
if(autoScroll){
|
||||
scrollToBottom();
|
||||
}
|
||||
}} onFilterChanged={props.onFilterChanged} />
|
||||
</div>
|
||||
<div
|
||||
ref={logsViewerRef}
|
||||
className="shadow-xl rounded-xl bg-slate-800 p-5 overflow-hidden hover:overflow-y-auto dark-scrollbar"
|
||||
style={{
|
||||
height: screenHeight - 330,
|
||||
}}
|
||||
>
|
||||
|
||||
{props.logs.map((log: Log, i: number) => {
|
||||
return <LogItem key={i} log={log} />;
|
||||
})}
|
||||
|
||||
{props.logs.length === 0 && (
|
||||
<div className={`text-slate-200 courier-prime`}>
|
||||
No logs found for this service.
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LogsViewer;
|
@ -14,7 +14,6 @@ import Field from './Field';
|
||||
import DetailField from '../Detail/Field';
|
||||
import Detail from '../Detail/Detail';
|
||||
import API from '../../Utils/API/API';
|
||||
import JSONFunctions from 'Common/Types/JSONFunctions';
|
||||
import ErrorMessage from '../ErrorMessage/ErrorMessage';
|
||||
import { useAsyncEffect } from 'use-async-effect';
|
||||
import User from '../../Utils/User';
|
||||
@ -258,7 +257,7 @@ const ModelDetail: <TBaseModel extends BaseModel>(
|
||||
return (
|
||||
<Detail
|
||||
id={props.id}
|
||||
item={JSONFunctions.toJSONObject(item, props.modelType)}
|
||||
item={BaseModel.toJSONObject(item, props.modelType)}
|
||||
fields={fields}
|
||||
showDetailsInNumberOfColumns={props.showDetailsInNumberOfColumns}
|
||||
/>
|
||||
|
@ -10,7 +10,6 @@ import { JSONObject } from 'Common/Types/JSON';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
import Alert, { AlertType } from '../Alerts/Alert';
|
||||
import FormValues from '../Forms/Types/FormValues';
|
||||
import JSONFunctions from 'Common/Types/JSONFunctions';
|
||||
import ModelAPI from '../../Utils/ModelAPI/ModelAPI';
|
||||
|
||||
export interface ComponentProps<TBaseModel extends BaseModel> {
|
||||
@ -94,7 +93,7 @@ const ModelFormModal: <TBaseModel extends BaseModel>(
|
||||
onSuccess={(data: TBaseModel) => {
|
||||
props.onSuccess &&
|
||||
props.onSuccess(
|
||||
JSONFunctions.fromJSONObject(
|
||||
BaseModel.fromJSONObject(
|
||||
data as TBaseModel,
|
||||
props.modelType
|
||||
)
|
||||
|
@ -15,7 +15,6 @@ import API from '../../Utils/API/API';
|
||||
import URL from 'Common/Types/API/URL';
|
||||
import { JSONArray } from 'Common/Types/JSON';
|
||||
import HTTPResponse from 'Common/Types/API/HTTPResponse';
|
||||
import JSONFunctions from 'Common/Types/JSONFunctions';
|
||||
|
||||
export interface ComponentProps<TBaseModel extends BaseModel> {
|
||||
query?: Query<TBaseModel>;
|
||||
@ -78,7 +77,7 @@ const ModelList: <TBaseModel extends BaseModel>(
|
||||
)) as HTTPResponse<JSONArray>;
|
||||
|
||||
listResult = {
|
||||
data: JSONFunctions.fromJSONArray(
|
||||
data: BaseModel.fromJSONArray(
|
||||
result.data as JSONArray,
|
||||
props.modelType
|
||||
),
|
||||
|
@ -52,7 +52,6 @@ import SubscriptionPlan, {
|
||||
} from 'Common/Types/Billing/SubscriptionPlan';
|
||||
import Pill from '../Pill/Pill';
|
||||
import { Yellow } from 'Common/Types/BrandColors';
|
||||
import JSONFunctions from 'Common/Types/JSONFunctions';
|
||||
import { ModalWidth } from '../Modal/Modal';
|
||||
import ProjectUtil from '../../Utils/Project';
|
||||
import API from '../../Utils/API/API';
|
||||
@ -304,7 +303,7 @@ const ModelTable: <TBaseModel extends BaseModel>(
|
||||
if (column.tooltipText) {
|
||||
tooltipText = (item: JSONObject): string => {
|
||||
return column.tooltipText!(
|
||||
JSONFunctions.fromJSONObject(item, props.modelType)
|
||||
BaseModel.fromJSONObject(item, props.modelType)
|
||||
);
|
||||
};
|
||||
}
|
||||
@ -479,7 +478,7 @@ const ModelTable: <TBaseModel extends BaseModel>(
|
||||
if (column.tooltipText) {
|
||||
classicColumn.tooltipText = (item: JSONObject): string => {
|
||||
return column.tooltipText!(
|
||||
JSONFunctions.fromJSONObject(item, props.modelType)
|
||||
BaseModel.fromJSONObject(item, props.modelType)
|
||||
);
|
||||
};
|
||||
}
|
||||
@ -853,13 +852,10 @@ const ModelTable: <TBaseModel extends BaseModel>(
|
||||
) => {
|
||||
try {
|
||||
const baseModel: TBaseModel =
|
||||
JSONFunctions.fromJSONObject(
|
||||
item,
|
||||
props.modelType
|
||||
);
|
||||
BaseModel.fromJSONObject(item, props.modelType);
|
||||
|
||||
if (props.onBeforeView) {
|
||||
item = JSONFunctions.toJSONObject(
|
||||
item = BaseModel.toJSONObject(
|
||||
await props.onBeforeView(baseModel),
|
||||
props.modelType
|
||||
);
|
||||
@ -913,9 +909,9 @@ const ModelTable: <TBaseModel extends BaseModel>(
|
||||
) => {
|
||||
try {
|
||||
if (props.onBeforeEdit) {
|
||||
item = JSONFunctions.toJSONObject(
|
||||
item = BaseModel.toJSONObject(
|
||||
await props.onBeforeEdit(
|
||||
JSONFunctions.fromJSONObject(
|
||||
BaseModel.fromJSONObject(
|
||||
item,
|
||||
props.modelType
|
||||
)
|
||||
@ -948,9 +944,9 @@ const ModelTable: <TBaseModel extends BaseModel>(
|
||||
) => {
|
||||
try {
|
||||
if (props.onBeforeDelete) {
|
||||
item = JSONFunctions.toJSONObject(
|
||||
item = BaseModel.toJSONObject(
|
||||
await props.onBeforeDelete(
|
||||
JSONFunctions.fromJSONObject(
|
||||
BaseModel.fromJSONObject(
|
||||
item,
|
||||
props.modelType
|
||||
)
|
||||
@ -1032,7 +1028,7 @@ const ModelTable: <TBaseModel extends BaseModel>(
|
||||
dragDropIdField={'_id'}
|
||||
dragDropIndexField={props.dragDropIndexField}
|
||||
totalItemsCount={totalItemsCount}
|
||||
data={JSONFunctions.toJSONObjectArray(data, props.modelType)}
|
||||
data={BaseModel.toJSONObjectArray(data, props.modelType)}
|
||||
filterError={tableFilterError}
|
||||
id={props.id}
|
||||
columns={tableColumns}
|
||||
@ -1108,7 +1104,7 @@ const ModelTable: <TBaseModel extends BaseModel>(
|
||||
<OrderedStatesList
|
||||
error={error}
|
||||
isLoading={isLoading}
|
||||
data={JSONFunctions.toJSONObjectArray(data, props.modelType)}
|
||||
data={BaseModel.toJSONObjectArray(data, props.modelType)}
|
||||
id={props.id}
|
||||
titleField={props.orderedStatesListProps?.titleField || ''}
|
||||
descriptionField={
|
||||
@ -1176,7 +1172,7 @@ const ModelTable: <TBaseModel extends BaseModel>(
|
||||
dragDropIndexField={props.dragDropIndexField}
|
||||
isLoading={isLoading}
|
||||
totalItemsCount={totalItemsCount}
|
||||
data={JSONFunctions.toJSONObjectArray(data, props.modelType)}
|
||||
data={BaseModel.toJSONObjectArray(data, props.modelType)}
|
||||
id={props.id}
|
||||
fields={fields}
|
||||
itemsOnPage={itemsOnPage}
|
||||
@ -1443,7 +1439,7 @@ const ModelTable: <TBaseModel extends BaseModel>(
|
||||
currentDeleteableItem['_id']
|
||||
) {
|
||||
deleteItem(
|
||||
JSONFunctions.fromJSON(
|
||||
BaseModel.fromJSON(
|
||||
currentDeleteableItem,
|
||||
props.modelType
|
||||
)
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { JSONObject } from 'Common/Types/JSON';
|
||||
import JSONFunctions from 'Common/Types/JSONFunctions';
|
||||
import React, { FunctionComponent, ReactElement } from 'react';
|
||||
import Image from '../Image/Image';
|
||||
import URL from 'Common/Types/API/URL';
|
||||
@ -7,6 +6,7 @@ import { FILE_URL } from '../../Config';
|
||||
import Probe from 'Model/Models/Probe';
|
||||
import Icon from '../Icon/Icon';
|
||||
import IconProp from 'Common/Types/Icon/IconProp';
|
||||
import BaseModel from 'Common/Models/BaseModel';
|
||||
|
||||
export interface ComponentProps {
|
||||
probe?: Probe | JSONObject | undefined | null;
|
||||
@ -18,7 +18,7 @@ const ProbeElement: FunctionComponent<ComponentProps> = (
|
||||
let probe: JSONObject | null | undefined = null;
|
||||
|
||||
if (props.probe instanceof Probe) {
|
||||
probe = JSONFunctions.toJSONObject(props.probe, Probe);
|
||||
probe = BaseModel.toJSONObject(props.probe, Probe);
|
||||
} else {
|
||||
probe = props.probe;
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ import {
|
||||
StatusPageRoute,
|
||||
WorkflowRoute,
|
||||
homeRoute,
|
||||
RealtimeRoute,
|
||||
} from 'Common/ServiceRoute';
|
||||
import Version from 'Common/Types/Version';
|
||||
import URL from 'Common/Types/API/URL';
|
||||
@ -48,6 +49,9 @@ export const NOTIFICATION_HOSTNAME: Hostname = Hostname.fromString(HOST);
|
||||
|
||||
export const DASHBOARD_HOSTNAME: Hostname = Hostname.fromString(HOST);
|
||||
|
||||
// realtime
|
||||
export const REALTIME_HOSTNAME: Hostname = Hostname.fromString(HOST);
|
||||
|
||||
export const INTEGRATION_HOSTNAME: Hostname = Hostname.fromString(HOST);
|
||||
|
||||
export const STATUS_PAGE_HOSTNAME: Hostname = Hostname.fromString(HOST);
|
||||
@ -72,6 +76,12 @@ export const DASHBOARD_API_URL: URL = new URL(
|
||||
DashboardApiRoute
|
||||
);
|
||||
|
||||
export const REALTIME_URL: URL = new URL(
|
||||
HTTP_PROTOCOL,
|
||||
REALTIME_HOSTNAME,
|
||||
RealtimeRoute
|
||||
);
|
||||
|
||||
export const IDENTITY_URL: URL = new URL(
|
||||
HTTP_PROTOCOL,
|
||||
IDENTITY_HOSTNAME,
|
||||
|
461
CommonUI/src/Utils/AnalyticsModelAPI/AnalyticsModelAPI.ts
Normal file
461
CommonUI/src/Utils/AnalyticsModelAPI/AnalyticsModelAPI.ts
Normal file
@ -0,0 +1,461 @@
|
||||
import AnalyticsBaseModel from 'Common/AnalyticsModels/BaseModel';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
import Query from './Query';
|
||||
import Select from './Select';
|
||||
import API from '../API/API';
|
||||
import Route from 'Common/Types/API/Route';
|
||||
import URL from 'Common/Types/API/URL';
|
||||
import BadDataException from 'Common/Types/Exception/BadDataException';
|
||||
import { DASHBOARD_API_URL } from '../../Config';
|
||||
import HTTPResponse from 'Common/Types/API/HTTPResponse';
|
||||
import HTTPMethod from 'Common/Types/API/HTTPMethod';
|
||||
import HTTPErrorResponse from 'Common/Types/API/HTTPErrorResponse';
|
||||
import { JSONArray, JSONObject } from 'Common/Types/JSON';
|
||||
import JSONFunctions from 'Common/Types/JSONFunctions';
|
||||
import { FormType } from '../../Components/Forms/ModelForm';
|
||||
import Dictionary from 'Common/Types/Dictionary';
|
||||
import ProjectUtil from '../Project';
|
||||
import Sort from './Sort';
|
||||
import Project from 'Model/Models/Project';
|
||||
import Navigation from '../Navigation';
|
||||
|
||||
export interface ListResult<TAnalyticsBaseModel extends AnalyticsBaseModel>
|
||||
extends JSONObject {
|
||||
data: Array<TAnalyticsBaseModel>;
|
||||
count: number;
|
||||
skip: number;
|
||||
limit: number;
|
||||
}
|
||||
|
||||
export interface RequestOptions {
|
||||
requestHeaders?: Dictionary<string> | undefined;
|
||||
overrideRequestUrl?: URL | undefined;
|
||||
}
|
||||
|
||||
export default class ModelAPI {
|
||||
public static async create<TAnalyticsBaseModel extends AnalyticsBaseModel>(
|
||||
model: TAnalyticsBaseModel,
|
||||
modelType: { new (): TAnalyticsBaseModel },
|
||||
requestOptions?: RequestOptions | undefined
|
||||
): Promise<
|
||||
HTTPResponse<
|
||||
| JSONObject
|
||||
| JSONArray
|
||||
| TAnalyticsBaseModel
|
||||
| Array<TAnalyticsBaseModel>
|
||||
>
|
||||
> {
|
||||
return await ModelAPI.createOrUpdate(
|
||||
model,
|
||||
modelType,
|
||||
FormType.Create,
|
||||
{},
|
||||
requestOptions
|
||||
);
|
||||
}
|
||||
|
||||
public static async update<TAnalyticsBaseModel extends AnalyticsBaseModel>(
|
||||
model: TAnalyticsBaseModel,
|
||||
modelType: { new (): TAnalyticsBaseModel }
|
||||
): Promise<
|
||||
HTTPResponse<
|
||||
| JSONObject
|
||||
| JSONArray
|
||||
| TAnalyticsBaseModel
|
||||
| Array<TAnalyticsBaseModel>
|
||||
>
|
||||
> {
|
||||
return await ModelAPI.createOrUpdate(model, modelType, FormType.Update);
|
||||
}
|
||||
|
||||
public static async updateById<
|
||||
TAnalyticsBaseModel extends AnalyticsBaseModel
|
||||
>(
|
||||
modelType: { new (): TAnalyticsBaseModel },
|
||||
id: ObjectID,
|
||||
data: JSONObject,
|
||||
apiUrlOverride?: URL,
|
||||
requestOptions?: RequestOptions
|
||||
): Promise<
|
||||
HTTPResponse<
|
||||
| JSONObject
|
||||
| JSONArray
|
||||
| TAnalyticsBaseModel
|
||||
| Array<TAnalyticsBaseModel>
|
||||
>
|
||||
> {
|
||||
const model: AnalyticsBaseModel = new modelType();
|
||||
let apiUrl: URL | null = apiUrlOverride || null;
|
||||
|
||||
if (!apiUrl) {
|
||||
const apiPath: Route | null = model.crudApiPath;
|
||||
if (!apiPath) {
|
||||
throw new BadDataException(
|
||||
'This model does not support create or update operations.'
|
||||
);
|
||||
}
|
||||
|
||||
apiUrl = URL.fromURL(DASHBOARD_API_URL).addRoute(apiPath);
|
||||
}
|
||||
|
||||
apiUrl = apiUrl.addRoute(`/${id.toString()}`);
|
||||
|
||||
const result: HTTPResponse<
|
||||
| JSONObject
|
||||
| JSONArray
|
||||
| TAnalyticsBaseModel
|
||||
| Array<TAnalyticsBaseModel>
|
||||
> = await API.fetch<
|
||||
| JSONObject
|
||||
| JSONArray
|
||||
| TAnalyticsBaseModel
|
||||
| Array<TAnalyticsBaseModel>
|
||||
>(
|
||||
HTTPMethod.PUT,
|
||||
apiUrl,
|
||||
{
|
||||
data: data,
|
||||
},
|
||||
this.getCommonHeaders(requestOptions)
|
||||
);
|
||||
|
||||
if (result.isSuccess()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
this.checkStatusCode(result);
|
||||
|
||||
throw result;
|
||||
}
|
||||
|
||||
public static async createOrUpdate<
|
||||
TAnalyticsBaseModel extends AnalyticsBaseModel
|
||||
>(
|
||||
model: TAnalyticsBaseModel,
|
||||
modelType: { new (): TAnalyticsBaseModel },
|
||||
formType: FormType,
|
||||
miscDataProps?: JSONObject,
|
||||
requestOptions?: RequestOptions | undefined
|
||||
): Promise<HTTPResponse<TAnalyticsBaseModel>> {
|
||||
let apiUrl: URL | null = requestOptions?.overrideRequestUrl || null;
|
||||
|
||||
if (!apiUrl) {
|
||||
const apiPath: Route | null = model.crudApiPath;
|
||||
if (!apiPath) {
|
||||
throw new BadDataException(
|
||||
'This model does not support create or update operations.'
|
||||
);
|
||||
}
|
||||
|
||||
apiUrl = URL.fromURL(DASHBOARD_API_URL).addRoute(apiPath);
|
||||
}
|
||||
|
||||
const httpMethod: HTTPMethod =
|
||||
formType === FormType.Create ? HTTPMethod.POST : HTTPMethod.PUT;
|
||||
|
||||
if (httpMethod === HTTPMethod.PUT) {
|
||||
apiUrl = apiUrl.addRoute(`/${model._id}`);
|
||||
}
|
||||
|
||||
const apiResult: HTTPErrorResponse | HTTPResponse<TAnalyticsBaseModel> =
|
||||
await API.fetch<TAnalyticsBaseModel>(
|
||||
httpMethod,
|
||||
apiUrl,
|
||||
{
|
||||
data: JSONFunctions.serialize(
|
||||
AnalyticsBaseModel.toJSON(model, modelType)
|
||||
),
|
||||
miscDataProps: miscDataProps || {},
|
||||
},
|
||||
{
|
||||
...this.getCommonHeaders(requestOptions),
|
||||
...(requestOptions?.requestHeaders || {}),
|
||||
}
|
||||
);
|
||||
|
||||
if (apiResult.isSuccess() && apiResult instanceof HTTPResponse) {
|
||||
const result: HTTPResponse<TAnalyticsBaseModel> =
|
||||
apiResult as HTTPResponse<TAnalyticsBaseModel>;
|
||||
|
||||
result.data = AnalyticsBaseModel.fromJSON(
|
||||
result.data,
|
||||
modelType
|
||||
) as TAnalyticsBaseModel;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
this.checkStatusCode(apiResult);
|
||||
|
||||
throw apiResult;
|
||||
}
|
||||
|
||||
public static async getList<TAnalyticsBaseModel extends AnalyticsBaseModel>(
|
||||
modelType: { new (): TAnalyticsBaseModel },
|
||||
query: Query<TAnalyticsBaseModel>,
|
||||
limit: number,
|
||||
skip: number,
|
||||
select: Select<TAnalyticsBaseModel>,
|
||||
sort: Sort<TAnalyticsBaseModel>,
|
||||
requestOptions?: RequestOptions
|
||||
): Promise<ListResult<TAnalyticsBaseModel>> {
|
||||
const model: TAnalyticsBaseModel = new modelType();
|
||||
const apiPath: Route | null = model.crudApiPath;
|
||||
if (!apiPath) {
|
||||
throw new BadDataException(
|
||||
'This model does not support list operations.'
|
||||
);
|
||||
}
|
||||
|
||||
let apiUrl: URL = URL.fromURL(DASHBOARD_API_URL)
|
||||
.addRoute(apiPath)
|
||||
.addRoute('/get-list');
|
||||
|
||||
if (requestOptions?.overrideRequestUrl) {
|
||||
apiUrl = requestOptions.overrideRequestUrl;
|
||||
}
|
||||
|
||||
if (!apiUrl) {
|
||||
throw new BadDataException(
|
||||
'This model does not support list operations.'
|
||||
);
|
||||
}
|
||||
|
||||
const headers: Dictionary<string> =
|
||||
this.getCommonHeaders(requestOptions);
|
||||
|
||||
const result: HTTPResponse<JSONArray> | HTTPErrorResponse =
|
||||
await API.fetch<JSONArray>(
|
||||
HTTPMethod.POST,
|
||||
apiUrl,
|
||||
{
|
||||
query: JSONFunctions.serialize(query as JSONObject),
|
||||
select: JSONFunctions.serialize(select as JSONObject),
|
||||
sort: JSONFunctions.serialize(sort as JSONObject),
|
||||
},
|
||||
headers,
|
||||
{
|
||||
limit: limit.toString(),
|
||||
skip: skip.toString(),
|
||||
}
|
||||
);
|
||||
|
||||
if (result.isSuccess()) {
|
||||
const list: Array<TAnalyticsBaseModel> =
|
||||
AnalyticsBaseModel.fromJSONArray(
|
||||
result.data as JSONArray,
|
||||
modelType
|
||||
);
|
||||
|
||||
return {
|
||||
data: list,
|
||||
count: result.count,
|
||||
skip: result.skip,
|
||||
limit: result.limit,
|
||||
};
|
||||
}
|
||||
|
||||
this.checkStatusCode(result);
|
||||
|
||||
throw result;
|
||||
}
|
||||
|
||||
public static async count<TAnalyticsBaseModel extends AnalyticsBaseModel>(
|
||||
modelType: { new (): TAnalyticsBaseModel },
|
||||
query: Query<TAnalyticsBaseModel>,
|
||||
requestOptions?: RequestOptions | undefined
|
||||
): Promise<number> {
|
||||
const model: TAnalyticsBaseModel = new modelType();
|
||||
const apiPath: Route | null = model.crudApiPath;
|
||||
if (!apiPath) {
|
||||
throw new BadDataException(
|
||||
'This model does not support list operations.'
|
||||
);
|
||||
}
|
||||
|
||||
let apiUrl: URL = URL.fromURL(DASHBOARD_API_URL)
|
||||
.addRoute(apiPath)
|
||||
.addRoute('/count');
|
||||
|
||||
if (requestOptions?.overrideRequestUrl) {
|
||||
apiUrl = requestOptions.overrideRequestUrl;
|
||||
}
|
||||
|
||||
if (!apiUrl) {
|
||||
throw new BadDataException(
|
||||
'This model does not support count operations.'
|
||||
);
|
||||
}
|
||||
|
||||
const headers: Dictionary<string> =
|
||||
this.getCommonHeaders(requestOptions);
|
||||
|
||||
const result: HTTPResponse<JSONObject> | HTTPErrorResponse =
|
||||
await API.fetch<JSONObject>(
|
||||
HTTPMethod.POST,
|
||||
apiUrl,
|
||||
{
|
||||
query: JSONFunctions.serialize(query as JSONObject),
|
||||
},
|
||||
headers
|
||||
);
|
||||
|
||||
if (result.isSuccess()) {
|
||||
const count: number = result.data['count'] as number;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
this.checkStatusCode(result);
|
||||
|
||||
throw result;
|
||||
}
|
||||
|
||||
public static getCommonHeaders(
|
||||
requestOptions?: RequestOptions
|
||||
): Dictionary<string> {
|
||||
let headers: Dictionary<string> = {};
|
||||
|
||||
if (!requestOptions || Object.keys(requestOptions).length === 0) {
|
||||
const project: Project | null = ProjectUtil.getCurrentProject();
|
||||
|
||||
if (project && project.id) {
|
||||
headers['tenantid'] = project.id.toString();
|
||||
}
|
||||
}
|
||||
|
||||
// add SSO headers.
|
||||
|
||||
headers = {
|
||||
...headers,
|
||||
};
|
||||
|
||||
return headers;
|
||||
}
|
||||
|
||||
public static async getItem<TAnalyticsBaseModel extends AnalyticsBaseModel>(
|
||||
modelType: { new (): TAnalyticsBaseModel },
|
||||
id: ObjectID,
|
||||
select: Select<TAnalyticsBaseModel>,
|
||||
requestOptions?: RequestOptions | undefined
|
||||
): Promise<TAnalyticsBaseModel | null> {
|
||||
const apiPath: Route | null = new modelType().crudApiPath;
|
||||
if (!apiPath) {
|
||||
throw new BadDataException(
|
||||
'This model does not support get operations.'
|
||||
);
|
||||
}
|
||||
|
||||
let apiUrl: URL = URL.fromURL(DASHBOARD_API_URL)
|
||||
.addRoute(apiPath)
|
||||
.addRoute('/' + id.toString())
|
||||
.addRoute('/get-item');
|
||||
|
||||
if (requestOptions?.overrideRequestUrl) {
|
||||
apiUrl = requestOptions.overrideRequestUrl;
|
||||
}
|
||||
|
||||
if (!apiUrl) {
|
||||
throw new BadDataException(
|
||||
'This model does not support get operations.'
|
||||
);
|
||||
}
|
||||
|
||||
return this.post<TAnalyticsBaseModel>(
|
||||
modelType,
|
||||
apiUrl,
|
||||
select,
|
||||
requestOptions
|
||||
);
|
||||
}
|
||||
|
||||
public static async post<TAnalyticsBaseModel extends AnalyticsBaseModel>(
|
||||
modelType: { new (): TAnalyticsBaseModel },
|
||||
apiUrl: URL,
|
||||
select?: Select<TAnalyticsBaseModel> | undefined,
|
||||
requestOptions?: RequestOptions | undefined
|
||||
): Promise<TAnalyticsBaseModel | null> {
|
||||
const result: HTTPResponse<TAnalyticsBaseModel> | HTTPErrorResponse =
|
||||
await API.fetch<TAnalyticsBaseModel>(
|
||||
HTTPMethod.POST,
|
||||
apiUrl,
|
||||
{
|
||||
select: JSONFunctions.serialize(select as JSONObject) || {},
|
||||
},
|
||||
this.getCommonHeaders(requestOptions)
|
||||
);
|
||||
|
||||
if (result.isSuccess()) {
|
||||
return AnalyticsBaseModel.fromJSON(
|
||||
result.data,
|
||||
modelType
|
||||
) as TAnalyticsBaseModel;
|
||||
}
|
||||
|
||||
this.checkStatusCode(result);
|
||||
|
||||
throw result;
|
||||
}
|
||||
|
||||
public static async deleteItem<
|
||||
TAnalyticsBaseModel extends AnalyticsBaseModel
|
||||
>(
|
||||
modelType: { new (): TAnalyticsBaseModel },
|
||||
id: ObjectID,
|
||||
requestOptions?: RequestOptions | undefined
|
||||
): Promise<void> {
|
||||
const apiPath: Route | null = new modelType().crudApiPath;
|
||||
if (!apiPath) {
|
||||
throw new BadDataException(
|
||||
'This model does not support delete operations.'
|
||||
);
|
||||
}
|
||||
|
||||
const apiUrl: URL = URL.fromURL(DASHBOARD_API_URL)
|
||||
.addRoute(apiPath)
|
||||
.addRoute('/' + id.toString());
|
||||
|
||||
if (!apiUrl) {
|
||||
throw new BadDataException(
|
||||
'This model does not support delete operations.'
|
||||
);
|
||||
}
|
||||
|
||||
const result: HTTPResponse<TAnalyticsBaseModel> | HTTPErrorResponse =
|
||||
await API.fetch<TAnalyticsBaseModel>(
|
||||
HTTPMethod.DELETE,
|
||||
apiUrl,
|
||||
undefined,
|
||||
this.getCommonHeaders(requestOptions)
|
||||
);
|
||||
|
||||
if (result.isSuccess()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.checkStatusCode(result);
|
||||
|
||||
throw result;
|
||||
}
|
||||
|
||||
private static checkStatusCode<
|
||||
TAnalyticsBaseModel extends AnalyticsBaseModel
|
||||
>(
|
||||
result:
|
||||
| HTTPResponse<
|
||||
| TAnalyticsBaseModel
|
||||
| JSONObject
|
||||
| JSONArray
|
||||
| Array<TAnalyticsBaseModel>
|
||||
>
|
||||
| HTTPErrorResponse
|
||||
): void {
|
||||
if (result.statusCode === 406) {
|
||||
const project: Project | null = ProjectUtil.getCurrentProject();
|
||||
|
||||
if (project && project.id) {
|
||||
Navigation.navigate(new Route(`/dashboard/${project._id}/sso`));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
17
CommonUI/src/Utils/AnalyticsModelAPI/Query.ts
Normal file
17
CommonUI/src/Utils/AnalyticsModelAPI/Query.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import AnalyticsDataModel from 'Common/AnalyticsModels/BaseModel';
|
||||
import CompareBase from 'Common/Types/Database/CompareBase';
|
||||
import InBetween from 'Common/Types/Database/InBetween';
|
||||
import NotNull from 'Common/Types/Database/NotNull';
|
||||
import Search from 'Common/Types/Database/Search';
|
||||
import { JSONObject, JSONValue } from 'Common/Types/JSON';
|
||||
|
||||
type Query<TBaseModel extends AnalyticsDataModel | JSONObject> = {
|
||||
[P in keyof TBaseModel]?:
|
||||
| JSONValue
|
||||
| Search
|
||||
| InBetween
|
||||
| NotNull
|
||||
| CompareBase;
|
||||
};
|
||||
|
||||
export default Query;
|
8
CommonUI/src/Utils/AnalyticsModelAPI/Select.ts
Normal file
8
CommonUI/src/Utils/AnalyticsModelAPI/Select.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import AnalyticsDataModel from 'Common/AnalyticsModels/BaseModel';
|
||||
import { JSONObject } from 'Common/Types/JSON';
|
||||
|
||||
type Select<TBaseModel extends AnalyticsDataModel | JSONObject> = {
|
||||
[P in keyof TBaseModel]?: boolean | JSONObject;
|
||||
};
|
||||
|
||||
export default Select;
|
9
CommonUI/src/Utils/AnalyticsModelAPI/Sort.ts
Normal file
9
CommonUI/src/Utils/AnalyticsModelAPI/Sort.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import AnalyticsDataModel from 'Common/AnalyticsModels/BaseModel';
|
||||
import SortOrder from 'Common/Types/BaseDatabase/SortOrder';
|
||||
import { JSONObject } from 'Common/Types/JSON';
|
||||
|
||||
type Sort<TBaseModel extends AnalyticsDataModel | JSONObject> = {
|
||||
[P in keyof TBaseModel]?: SortOrder;
|
||||
};
|
||||
|
||||
export default Sort;
|
@ -144,7 +144,7 @@ export default class ModelAPI {
|
||||
apiUrl,
|
||||
{
|
||||
data: JSONFunctions.serialize(
|
||||
JSONFunctions.toJSON(model, modelType)
|
||||
BaseModel.toJSON(model, modelType)
|
||||
),
|
||||
miscDataProps: miscDataProps || {},
|
||||
},
|
||||
@ -165,7 +165,7 @@ export default class ModelAPI {
|
||||
delete (result.data as any)['_miscData'];
|
||||
}
|
||||
|
||||
result.data = JSONFunctions.fromJSONObject(result.data, modelType);
|
||||
result.data = BaseModel.fromJSONObject(result.data, modelType);
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -229,7 +229,7 @@ export default class ModelAPI {
|
||||
);
|
||||
|
||||
if (result.isSuccess()) {
|
||||
const list: Array<TBaseModel> = JSONFunctions.fromJSONArray(
|
||||
const list: Array<TBaseModel> = BaseModel.fromJSONArray(
|
||||
result.data as JSONArray,
|
||||
modelType
|
||||
);
|
||||
@ -306,7 +306,7 @@ export default class ModelAPI {
|
||||
): Dictionary<string> {
|
||||
let headers: Dictionary<string> = {};
|
||||
|
||||
if (!requestOptions || !requestOptions.isMultiTenantRequest) {
|
||||
if (!requestOptions || !requestOptions.isMultiTenantRequest || Object.keys(requestOptions).length === 0) {
|
||||
const project: Project | null = ProjectUtil.getCurrentProject();
|
||||
|
||||
if (project && project.id) {
|
||||
@ -375,7 +375,7 @@ export default class ModelAPI {
|
||||
);
|
||||
|
||||
if (result.isSuccess()) {
|
||||
return JSONFunctions.fromJSONObject(
|
||||
return BaseModel.fromJSONObject(
|
||||
result.data as JSONObject,
|
||||
modelType
|
||||
);
|
||||
|
@ -1,12 +1,12 @@
|
||||
import LocalStorage from './LocalStorage';
|
||||
import { JSONObject } from 'Common/Types/JSON';
|
||||
import Project from 'Model/Models/Project';
|
||||
import JSONFunctions from 'Common/Types/JSONFunctions';
|
||||
import SubscriptionPlan, {
|
||||
PlanSelect,
|
||||
} from 'Common/Types/Billing/SubscriptionPlan';
|
||||
import { BILLING_ENABLED, getAllEnvVars } from '../Config';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
import BaseModel from 'Common/Models/BaseModel';
|
||||
|
||||
export default class ProjectUtil {
|
||||
public static getCurrentProject(): Project | null {
|
||||
@ -16,7 +16,7 @@ export default class ProjectUtil {
|
||||
const projectJson: JSONObject = LocalStorage.getItem(
|
||||
'current_project'
|
||||
) as JSONObject;
|
||||
return JSONFunctions.fromJSON(projectJson, Project) as Project;
|
||||
return BaseModel.fromJSON(projectJson, Project) as Project;
|
||||
}
|
||||
|
||||
public static getCurrentProjectId(): ObjectID | null {
|
||||
@ -25,7 +25,7 @@ export default class ProjectUtil {
|
||||
|
||||
public static setCurrentProject(project: JSONObject | Project): void {
|
||||
if (project instanceof Project) {
|
||||
project = JSONFunctions.toJSON(project, Project);
|
||||
project = BaseModel.toJSON(project, Project);
|
||||
}
|
||||
LocalStorage.setItem('current_project', project);
|
||||
}
|
||||
|
141
CommonUI/src/Utils/Realtime.ts
Normal file
141
CommonUI/src/Utils/Realtime.ts
Normal file
@ -0,0 +1,141 @@
|
||||
import AnalyticsBaseModel from 'Common/AnalyticsModels/BaseModel';
|
||||
import BaseModel from 'Common/Models/BaseModel';
|
||||
import AnalyticsQuery from './AnalyticsModelAPI/Query';
|
||||
import Query from './ModelAPI/Query';
|
||||
import RealtimeUtil, {
|
||||
EventName,
|
||||
ListenToModelEventJSON,
|
||||
ModelEventType,
|
||||
} from 'Common/Utils/Realtime';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
import SocketIO, { Socket } from 'socket.io-client';
|
||||
import { HOST, HTTP_PROTOCOL } from '../Config';
|
||||
import URL from 'Common/Types/API/URL';
|
||||
import JSONFunctions from 'Common/Types/JSONFunctions';
|
||||
import DatabaseType from 'Common/Types/BaseDatabase/DatabaseType';
|
||||
import AnalyticsSelect from './AnalyticsModelAPI/Select';
|
||||
import Select from './ModelAPI/Select';
|
||||
import { JSONObject } from 'Common/Types/JSON';
|
||||
import { RealtimeRoute } from 'Common/ServiceRoute';
|
||||
|
||||
export interface ListenToAnalyticsModelEvent<Model extends AnalyticsBaseModel> {
|
||||
modelType: { new (): Model };
|
||||
query: AnalyticsQuery<Model>;
|
||||
eventType: ModelEventType;
|
||||
tenantId: ObjectID;
|
||||
select: AnalyticsSelect<Model>;
|
||||
}
|
||||
|
||||
export interface ListenToModelEvent<Model extends BaseModel> {
|
||||
modelType: { new (): Model };
|
||||
query: Query<Model>;
|
||||
tenantId: ObjectID;
|
||||
eventType: ModelEventType;
|
||||
select: Select<Model>;
|
||||
}
|
||||
|
||||
export default abstract class Reatime {
|
||||
private static socket: Socket;
|
||||
|
||||
public static init(): void {
|
||||
const socket: Socket = SocketIO(
|
||||
new URL(HTTP_PROTOCOL, HOST).toString(),
|
||||
{
|
||||
path: RealtimeRoute.toString(),
|
||||
}
|
||||
);
|
||||
|
||||
this.socket = socket;
|
||||
}
|
||||
|
||||
public static listenToModelEvent<Model extends BaseModel>(
|
||||
listenToModelEvent: ListenToModelEvent<Model>,
|
||||
onEvent: (model: Model) => void
|
||||
): () => void {
|
||||
// conver this to json and send it to the server.
|
||||
|
||||
if (!this.socket) {
|
||||
this.init();
|
||||
}
|
||||
|
||||
const listenToModelEventJSON: ListenToModelEventJSON = {
|
||||
eventType: listenToModelEvent.eventType,
|
||||
modelType: DatabaseType.Database,
|
||||
modelName: listenToModelEvent.modelType.name,
|
||||
query: JSONFunctions.serialize(listenToModelEvent.query),
|
||||
tenantId: listenToModelEvent.tenantId.toString(),
|
||||
select: JSONFunctions.serialize(listenToModelEvent.select),
|
||||
};
|
||||
|
||||
this.emit(EventName.ListenToModalEvent, listenToModelEventJSON as any);
|
||||
|
||||
const roomId: string = RealtimeUtil.getRoomId(
|
||||
listenToModelEvent.tenantId,
|
||||
listenToModelEvent.modelType.name,
|
||||
listenToModelEvent.eventType
|
||||
);
|
||||
|
||||
this.socket.on(roomId, (model: JSONObject) => {
|
||||
onEvent(
|
||||
BaseModel.fromJSON(model, listenToModelEvent.modelType) as Model
|
||||
);
|
||||
});
|
||||
|
||||
// Stop listening to the event.
|
||||
const stopListening: () => void = (): void => {
|
||||
this.socket.off(roomId);
|
||||
};
|
||||
|
||||
return stopListening;
|
||||
}
|
||||
|
||||
public static listenToAnalyticsModelEvent<Model extends AnalyticsBaseModel>(
|
||||
listenToModelEvent: ListenToAnalyticsModelEvent<Model>,
|
||||
onEvent: (model: Model) => void
|
||||
): () => void {
|
||||
if (!this.socket) {
|
||||
this.init();
|
||||
}
|
||||
|
||||
const listenToModelEventJSON: ListenToModelEventJSON = {
|
||||
eventType: listenToModelEvent.eventType,
|
||||
modelType: DatabaseType.AnalyticsDatabase,
|
||||
modelName: listenToModelEvent.modelType.name,
|
||||
query: JSONFunctions.serialize(listenToModelEvent.query),
|
||||
tenantId: listenToModelEvent.tenantId.toString(),
|
||||
select: JSONFunctions.serialize(listenToModelEvent.select),
|
||||
};
|
||||
|
||||
this.emit(EventName.ListenToModalEvent, listenToModelEventJSON as any);
|
||||
|
||||
const roomId: string = RealtimeUtil.getRoomId(
|
||||
listenToModelEvent.tenantId,
|
||||
listenToModelEvent.modelType.name,
|
||||
listenToModelEvent.eventType
|
||||
);
|
||||
|
||||
this.socket.on(roomId, (model: JSONObject) => {
|
||||
onEvent(
|
||||
AnalyticsBaseModel.fromJSON(
|
||||
model,
|
||||
listenToModelEvent.modelType
|
||||
) as Model
|
||||
);
|
||||
});
|
||||
|
||||
// Stop listening to the event.
|
||||
const stopListening: () => void = (): void => {
|
||||
this.socket.off(roomId);
|
||||
};
|
||||
|
||||
return stopListening;
|
||||
}
|
||||
|
||||
public static emit(eventName: string, data: JSONObject): void {
|
||||
if (!this.socket) {
|
||||
this.init();
|
||||
}
|
||||
|
||||
this.socket.emit(eventName, data);
|
||||
}
|
||||
}
|
@ -34,6 +34,7 @@
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700;800;900&display=swap" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Courier+Prime:wght@100;200;300;400;500;600;700;800;900&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
* {
|
||||
font-family: Inter;
|
||||
@ -65,6 +66,15 @@
|
||||
top: 0;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.courier-prime {
|
||||
font-family: 'Courier Prime', monospace;
|
||||
}
|
||||
|
||||
.dark-scrollbar {
|
||||
scrollbar-color: #475569 #0f172a;
|
||||
}
|
||||
|
||||
</style>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
|
||||
|
@ -187,6 +187,8 @@ import TelemetryServiceView from './Pages/Telemetry/Services/View/Index';
|
||||
import TelemetryServiceViewDelete from './Pages/Telemetry/Services/View/Delete';
|
||||
import TelemetryServiceViewLogs from './Pages/Telemetry/Services/View/Logs/Index';
|
||||
import TelemetryServiceViewTraces from './Pages/Telemetry/Services/View/Traces/Index';
|
||||
import TelemetryServiceViewMetrics from './Pages/Telemetry/Services/View/Metrics/Index';
|
||||
import TelemetryServiceViewDahboard from './Pages/Telemetry/Services/View/Dashboard/Index';
|
||||
|
||||
const App: () => JSX.Element = () => {
|
||||
Navigation.setNavigateHook(useNavigate());
|
||||
@ -474,6 +476,42 @@ const App: () => JSX.Element = () => {
|
||||
}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={
|
||||
RouteMap[
|
||||
PageMap.TELEMETRY_SERVICES_VIEW_METRICS
|
||||
]?.toString() || ''
|
||||
}
|
||||
element={
|
||||
<TelemetryServiceViewMetrics
|
||||
{...commonPageProps}
|
||||
pageRoute={
|
||||
RouteMap[
|
||||
PageMap.TELEMETRY_SERVICES_VIEW_METRICS
|
||||
] as Route
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={
|
||||
RouteMap[
|
||||
PageMap.TELEMETRY_SERVICES_VIEW_DASHBOARDS
|
||||
]?.toString() || ''
|
||||
}
|
||||
element={
|
||||
<TelemetryServiceViewDahboard
|
||||
{...commonPageProps}
|
||||
pageRoute={
|
||||
RouteMap[
|
||||
PageMap.TELEMETRY_SERVICES_VIEW_DASHBOARDS
|
||||
] as Route
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
{/* Home */}
|
||||
|
||||
<PageRoute
|
||||
|
@ -15,7 +15,6 @@ import LabelsElement from '../../Components/Label/Labels';
|
||||
import IncidentSeverity from 'Model/Models/IncidentSeverity';
|
||||
import Query from 'CommonUI/src/Utils/ModelAPI/Query';
|
||||
import Route from 'Common/Types/API/Route';
|
||||
import JSONFunctions from 'Common/Types/JSONFunctions';
|
||||
import GlobalEvents from 'CommonUI/src/Utils/GlobalEvents';
|
||||
import EventName from '../../Utils/EventName';
|
||||
import DashboardNavigation from '../../Utils/Navigation';
|
||||
@ -34,6 +33,7 @@ import IncidentTemplateOwnerUser from 'Model/Models/IncidentTemplateOwnerUser';
|
||||
import IncidentTemplateOwnerTeam from 'Model/Models/IncidentTemplateOwnerTeam';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
import { ButtonStyleType } from 'CommonUI/src/Components/Button/Button';
|
||||
import BaseModel from 'Common/Models/BaseModel';
|
||||
|
||||
export interface ComponentProps {
|
||||
query?: Query<Incident> | undefined;
|
||||
@ -108,7 +108,7 @@ const IncidentsTable: FunctionComponent<ComponentProps> = (
|
||||
|
||||
if (incidentTemplate) {
|
||||
const initialValue: JSONObject = {
|
||||
...JSONFunctions.toJSONObject(
|
||||
...BaseModel.toJSONObject(
|
||||
incidentTemplate,
|
||||
IncidentTemplate
|
||||
),
|
||||
@ -519,7 +519,7 @@ const IncidentsTable: FunctionComponent<ComponentProps> = (
|
||||
return (
|
||||
<MonitorsElement
|
||||
monitors={
|
||||
JSONFunctions.fromJSON(
|
||||
BaseModel.fromJSON(
|
||||
(item['monitors'] as JSONArray) ||
|
||||
[],
|
||||
Monitor
|
||||
@ -560,7 +560,7 @@ const IncidentsTable: FunctionComponent<ComponentProps> = (
|
||||
return (
|
||||
<LabelsElement
|
||||
labels={
|
||||
JSONFunctions.fromJSON(
|
||||
BaseModel.fromJSON(
|
||||
(item['labels'] as JSONArray) || [],
|
||||
Label
|
||||
) as Array<Label>
|
||||
|
117
Dashboard/src/Components/LogsViewer/LogsViewer.tsx
Normal file
117
Dashboard/src/Components/LogsViewer/LogsViewer.tsx
Normal file
@ -0,0 +1,117 @@
|
||||
import React, { FunctionComponent, ReactElement, useEffect } from 'react';
|
||||
import LogsViewer from 'CommonUI/src/Components/LogsViewer/LogsViewer';
|
||||
import Log from 'Model/AnalyticsModels/Log';
|
||||
import ErrorMessage from 'CommonUI/src/Components/ErrorMessage/ErrorMessage';
|
||||
import AnalyticsModelAPI, {
|
||||
ListResult,
|
||||
} from 'CommonUI/src/Utils/AnalyticsModelAPI/AnalyticsModelAPI';
|
||||
import ComponentLoader from 'CommonUI/src/Components/ComponentLoader/ComponentLoader';
|
||||
import API from 'CommonUI/src/Utils/API/API';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
import { LIMIT_PER_PROJECT } from 'Common/Types/Database/LimitMax';
|
||||
import SortOrder from 'Common/Types/BaseDatabase/SortOrder';
|
||||
import Realtime from 'CommonUI/src/Utils/Realtime';
|
||||
import { ModelEventType } from 'Common/Utils/Realtime';
|
||||
import ProjectUtil from 'CommonUI/src/Utils/Project';
|
||||
|
||||
export interface ComponentProps {
|
||||
id: string;
|
||||
telemetryServiceIds: Array<ObjectID>;
|
||||
}
|
||||
|
||||
const DashboardLogsViewer: FunctionComponent<ComponentProps> = (
|
||||
props: ComponentProps
|
||||
): ReactElement => {
|
||||
const [logs, setLogs] = React.useState<Array<Log>>([]);
|
||||
const [error, setError] = React.useState<string>('');
|
||||
const [isLoading, setIsLoading] = React.useState<boolean>(false);
|
||||
|
||||
const fetchItems: Function = async () => {
|
||||
setError('');
|
||||
setIsLoading(true);
|
||||
|
||||
try {
|
||||
const listResult: ListResult<Log> =
|
||||
await AnalyticsModelAPI.getList<Log>(
|
||||
Log,
|
||||
{
|
||||
serviceId: props.telemetryServiceIds[0],
|
||||
},
|
||||
LIMIT_PER_PROJECT,
|
||||
0,
|
||||
{
|
||||
body: true,
|
||||
time: true,
|
||||
projectId: true,
|
||||
serviceId: true,
|
||||
spanId: true,
|
||||
traceId: true,
|
||||
severityText: true,
|
||||
},
|
||||
{
|
||||
time: SortOrder.Descending,
|
||||
},
|
||||
{}
|
||||
);
|
||||
|
||||
// reverse the logs so that the newest logs are at the bottom
|
||||
listResult.data.reverse();
|
||||
|
||||
setLogs(listResult.data);
|
||||
} catch (err) {
|
||||
setError(API.getFriendlyMessage(err));
|
||||
}
|
||||
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchItems().catch((err: unknown) => {
|
||||
setError(API.getFriendlyMessage(err));
|
||||
});
|
||||
|
||||
const disconnectFunction: () => void =
|
||||
Realtime.listenToAnalyticsModelEvent(
|
||||
{
|
||||
modelType: Log,
|
||||
query: {},
|
||||
eventType: ModelEventType.Create,
|
||||
tenantId: ProjectUtil.getCurrentProjectId()!,
|
||||
select: {
|
||||
body: true,
|
||||
time: true,
|
||||
projectId: true,
|
||||
serviceId: true,
|
||||
spanId: true,
|
||||
traceId: true,
|
||||
severityText: true,
|
||||
},
|
||||
},
|
||||
(model: Log) => {
|
||||
setLogs((logs: Array<Log>) => {
|
||||
return [...logs, model];
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
return () => {
|
||||
disconnectFunction();
|
||||
};
|
||||
}, []);
|
||||
|
||||
if (error) {
|
||||
return <ErrorMessage error={error} />;
|
||||
}
|
||||
|
||||
if (isLoading) {
|
||||
return <ComponentLoader />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div id={props.id}>
|
||||
<LogsViewer onFilterChanged={()=>{}} logs={logs} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardLogsViewer;
|
@ -13,7 +13,6 @@ import MonitorStatus from 'Model/Models/MonitorStatus';
|
||||
import Query from 'CommonUI/src/Utils/ModelAPI/Query';
|
||||
import Route from 'Common/Types/API/Route';
|
||||
import MonitorType from 'Common/Types/Monitor/MonitorType';
|
||||
import JSONFunctions from 'Common/Types/JSONFunctions';
|
||||
import DashboardNavigation from '../../Utils/Navigation';
|
||||
import MonitorTypeUtil from '../../Utils/MonitorType';
|
||||
import FormValues from 'CommonUI/src/Components/Forms/Types/FormValues';
|
||||
@ -26,6 +25,7 @@ import { ModalWidth } from 'CommonUI/src/Components/Modal/Modal';
|
||||
import MonitoringInterval from '../../Utils/MonitorIntervalDropdownOptions';
|
||||
import MonitorStepsType from 'Common/Types/Monitor/MonitorSteps';
|
||||
import { Grey } from 'Common/Types/BrandColors';
|
||||
import BaseModel from 'Common/Models/BaseModel';
|
||||
|
||||
export interface ComponentProps {
|
||||
query?: Query<Monitor> | undefined;
|
||||
@ -266,7 +266,7 @@ const MonitorsTable: FunctionComponent<ComponentProps> = (
|
||||
return (
|
||||
<LabelsElement
|
||||
labels={
|
||||
JSONFunctions.fromJSON(
|
||||
BaseModel.fromJSON(
|
||||
(item['labels'] as JSONArray) || [],
|
||||
Label
|
||||
) as Array<Label>
|
||||
|
@ -129,7 +129,7 @@ const DashboardNavbar: FunctionComponent<ComponentProps> = (
|
||||
),
|
||||
}}
|
||||
>
|
||||
{/* <NavBarMenuItem
|
||||
<NavBarMenuItem
|
||||
title="Telemetry"
|
||||
description="Logs, Traces, Metrics and more."
|
||||
route={RouteUtil.populateRouteParams(
|
||||
@ -139,7 +139,7 @@ const DashboardNavbar: FunctionComponent<ComponentProps> = (
|
||||
onClick={() => {
|
||||
forceHideMoreMenu();
|
||||
}}
|
||||
/> */}
|
||||
/>
|
||||
|
||||
<NavBarMenuItem
|
||||
title="On-Call Duty"
|
||||
|
@ -1,7 +1,6 @@
|
||||
import React, { FunctionComponent, ReactElement } from 'react';
|
||||
import BaseModel from 'Common/Models/BaseModel';
|
||||
import { JSONObject } from 'Common/Types/JSON';
|
||||
import JSONFunctions from 'Common/Types/JSONFunctions';
|
||||
|
||||
export interface ComponentProps {
|
||||
item: JSONObject;
|
||||
@ -11,7 +10,7 @@ export interface ComponentProps {
|
||||
const NotificationMethodView: FunctionComponent<ComponentProps> = (
|
||||
props: ComponentProps
|
||||
): ReactElement => {
|
||||
const item: BaseModel = JSONFunctions.fromJSONObject(
|
||||
const item: BaseModel = BaseModel.fromJSONObject(
|
||||
props.item,
|
||||
props.modelType
|
||||
);
|
||||
|
@ -12,7 +12,6 @@ import IncidentView from '../../../Components/Incident/Incident';
|
||||
import Incident from 'Model/Models/Incident';
|
||||
import OnCallDutyPolicyStatus from 'Common/Types/OnCallDutyPolicy/OnCallDutyPolicyStatus';
|
||||
import UserElement from '../../../Components/User/User';
|
||||
import JSONFunctions from 'Common/Types/JSONFunctions';
|
||||
import User from 'Model/Models/User';
|
||||
import DropdownUtil from 'CommonUI/src/Utils/Dropdown';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
@ -21,6 +20,7 @@ import Columns from 'CommonUI/src/Components/ModelTable/Columns';
|
||||
import OnCallPolicyView from '../OnCallPolicy';
|
||||
import OnCallDutyPolicy from 'Model/Models/OnCallDutyPolicy';
|
||||
import Navigation from 'CommonUI/src/Utils/Navigation';
|
||||
import BaseModel from 'Common/Models/BaseModel';
|
||||
|
||||
export interface ComponentProps {
|
||||
onCallDutyPolicyId?: ObjectID | undefined; // if this is undefined. then it'll show logs for all policies.
|
||||
@ -163,7 +163,7 @@ const ExecutionLogsTable: FunctionComponent<ComponentProps> = (
|
||||
return (
|
||||
<UserElement
|
||||
user={
|
||||
JSONFunctions.fromJSON(
|
||||
BaseModel.fromJSON(
|
||||
item['acknowledgedByUser'] as JSONObject,
|
||||
User
|
||||
) as User
|
||||
|
@ -11,11 +11,11 @@ import OnCallDutyPolicyExecutionLogTimeline from 'Model/Models/OnCallDutyPolicyE
|
||||
import OnCallDutyExecutionLogTimelineStatus from 'Common/Types/OnCallDutyPolicy/OnCalDutyExecutionLogTimelineStatus';
|
||||
import UserElement from '../../User/User';
|
||||
import User from 'Model/Models/User';
|
||||
import JSONFunctions from 'Common/Types/JSONFunctions';
|
||||
import EscalationRule from '../EscalationRule/EscalationRule';
|
||||
import OnCallDutyPolicyEscalationRule from 'Model/Models/OnCallDutyPolicyEscalationRule';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
import DropdownUtil from 'CommonUI/src/Utils/Dropdown';
|
||||
import BaseModel from 'Common/Models/BaseModel';
|
||||
|
||||
export interface ComponentProps {
|
||||
onCallPolicyExecutionLogId: ObjectID;
|
||||
@ -127,7 +127,7 @@ const ExecutionLogTimelineTable: FunctionComponent<ComponentProps> = (
|
||||
return (
|
||||
<UserElement
|
||||
user={
|
||||
JSONFunctions.fromJSON(
|
||||
BaseModel.fromJSON(
|
||||
item[
|
||||
'alertSentToUser'
|
||||
] as JSONObject,
|
||||
|
@ -16,11 +16,11 @@ import Route from 'Common/Types/API/Route';
|
||||
import StatusPage from 'Model/Models/StatusPage';
|
||||
import StatusPagesElement from '../StatusPage/StatusPagesLabel';
|
||||
import MonitorStatus from 'Model/Models/MonitorStatus';
|
||||
import JSONFunctions from 'Common/Types/JSONFunctions';
|
||||
import DashboardNavigation from '../../Utils/Navigation';
|
||||
import OneUptimeDate from 'Common/Types/Date';
|
||||
import Team from 'Model/Models/Team';
|
||||
import ProjectUser from '../../Utils/ProjectUser';
|
||||
import BaseModel from 'Common/Models/BaseModel';
|
||||
|
||||
export interface ComponentProps {
|
||||
query?: Query<ScheduledMaintenance> | undefined;
|
||||
@ -316,7 +316,7 @@ const ScheduledMaintenancesTable: FunctionComponent<ComponentProps> = (
|
||||
return (
|
||||
<MonitorsElement
|
||||
monitors={
|
||||
JSONFunctions.fromJSON(
|
||||
BaseModel.fromJSON(
|
||||
(item['monitors'] as JSONArray) || [],
|
||||
Monitor
|
||||
) as Array<Monitor>
|
||||
@ -349,7 +349,7 @@ const ScheduledMaintenancesTable: FunctionComponent<ComponentProps> = (
|
||||
return (
|
||||
<StatusPagesElement
|
||||
statusPages={
|
||||
JSONFunctions.fromJSON(
|
||||
BaseModel.fromJSON(
|
||||
(item['statusPages'] as JSONArray) ||
|
||||
[],
|
||||
StatusPage
|
||||
@ -398,7 +398,7 @@ const ScheduledMaintenancesTable: FunctionComponent<ComponentProps> = (
|
||||
return (
|
||||
<LabelsElement
|
||||
labels={
|
||||
JSONFunctions.fromJSON(
|
||||
BaseModel.fromJSON(
|
||||
(item['labels'] as JSONArray) || [],
|
||||
Label
|
||||
) as Array<Label>
|
||||
|
@ -7,6 +7,7 @@ import URL from 'Common/Types/API/URL';
|
||||
import { FILE_URL } from 'CommonUI/src/Config';
|
||||
import BlankProfilePic from 'CommonUI/src/Images/users/blank-profile.svg';
|
||||
import Route from 'Common/Types/API/Route';
|
||||
import BaseModel from 'Common/Models/BaseModel';
|
||||
|
||||
export interface ComponentProps {
|
||||
user?: User | JSONObject | undefined | null;
|
||||
@ -23,7 +24,7 @@ const UserElement: FunctionComponent<ComponentProps> = (
|
||||
let user: JSONObject | null | undefined = null;
|
||||
|
||||
if (props.user instanceof User) {
|
||||
user = JSONFunctions.toJSONObject(props.user, User);
|
||||
user = BaseModel.toJSONObject(props.user, User);
|
||||
} else {
|
||||
user = props.user;
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ import Monitor from 'Model/Models/Monitor';
|
||||
import Color from 'Common/Types/Color';
|
||||
import ProjectElement from '../../Components/Project/Project';
|
||||
import Project from 'Model/Models/Project';
|
||||
import JSONFunctions from 'Common/Types/JSONFunctions';
|
||||
import BaseModel from 'Common/Models/BaseModel';
|
||||
|
||||
const Home: FunctionComponent<PageComponentProps> = (
|
||||
_props: PageComponentProps
|
||||
@ -90,7 +90,7 @@ const Home: FunctionComponent<PageComponentProps> = (
|
||||
return (
|
||||
<ProjectElement
|
||||
project={
|
||||
JSONFunctions.fromJSON(
|
||||
BaseModel.fromJSON(
|
||||
(item['project'] as JSONObject) ||
|
||||
[],
|
||||
Project
|
||||
@ -198,7 +198,7 @@ const Home: FunctionComponent<PageComponentProps> = (
|
||||
return (
|
||||
<MonitorsElement
|
||||
monitors={
|
||||
JSONFunctions.fromJSON(
|
||||
BaseModel.fromJSON(
|
||||
(item['monitors'] as JSONArray) ||
|
||||
[],
|
||||
Monitor
|
||||
|
@ -26,7 +26,6 @@ import BaseModel from 'Common/Models/BaseModel';
|
||||
import IncidentSeverity from 'Model/Models/IncidentSeverity';
|
||||
import Label from 'Model/Models/Label';
|
||||
import LabelsElement from '../../../Components/Label/Labels';
|
||||
import JSONFunctions from 'Common/Types/JSONFunctions';
|
||||
import GlobalEvent from 'CommonUI/src/Utils/GlobalEvents';
|
||||
import EventName from '../../../Utils/EventName';
|
||||
import OnCallDutyPoliciesView from '../../../Components/OnCallPolicy/OnCallPolicies';
|
||||
@ -271,7 +270,7 @@ const IncidentView: FunctionComponent<PageComponentProps> = (
|
||||
return (
|
||||
<MonitorsElement
|
||||
monitors={
|
||||
JSONFunctions.fromJSON(
|
||||
BaseModel.fromJSON(
|
||||
(item[
|
||||
'monitors'
|
||||
] as JSONArray) || [],
|
||||
@ -295,7 +294,7 @@ const IncidentView: FunctionComponent<PageComponentProps> = (
|
||||
return (
|
||||
<OnCallDutyPoliciesView
|
||||
onCallPolicies={
|
||||
JSONFunctions.fromJSON(
|
||||
BaseModel.fromJSON(
|
||||
(item[
|
||||
'onCallDutyPolicies'
|
||||
] as JSONArray) || [],
|
||||
@ -326,7 +325,7 @@ const IncidentView: FunctionComponent<PageComponentProps> = (
|
||||
return (
|
||||
<LabelsElement
|
||||
labels={
|
||||
JSONFunctions.fromJSON(
|
||||
BaseModel.fromJSON(
|
||||
(item['labels'] as JSONArray) ||
|
||||
[],
|
||||
Label
|
||||
|
@ -7,6 +7,7 @@ import PageComponentProps from '../../PageComponentProps';
|
||||
import SideMenu from './SideMenu';
|
||||
import DashboardNavigation from '../../../Utils/Navigation';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
import BaseModel from 'Common/Models/BaseModel';
|
||||
import IncidentInternalNote from 'Model/Models/IncidentInternalNote';
|
||||
import ModelTable, {
|
||||
ShowTableAs,
|
||||
@ -17,7 +18,6 @@ import FieldType from 'CommonUI/src/Components/Types/FieldType';
|
||||
import { JSONObject } from 'Common/Types/JSON';
|
||||
import UserElement from '../../../Components/User/User';
|
||||
import User from 'Model/Models/User';
|
||||
import JSONFunctions from 'Common/Types/JSONFunctions';
|
||||
import Navigation from 'CommonUI/src/Utils/Navigation';
|
||||
import AlignItem from 'CommonUI/src/Types/AlignItem';
|
||||
import { ModalWidth } from 'CommonUI/src/Components/Modal/Modal';
|
||||
@ -67,7 +67,7 @@ const IncidentDelete: FunctionComponent<PageComponentProps> = (
|
||||
|
||||
if (incidentNoteTemplate) {
|
||||
const initialValue: JSONObject = {
|
||||
...JSONFunctions.toJSONObject(
|
||||
...BaseModel.toJSONObject(
|
||||
incidentNoteTemplate,
|
||||
IncidentNoteTemplate
|
||||
),
|
||||
@ -228,7 +228,7 @@ const IncidentDelete: FunctionComponent<PageComponentProps> = (
|
||||
return (
|
||||
<UserElement
|
||||
user={
|
||||
JSONFunctions.fromJSON(
|
||||
BaseModel.fromJSON(
|
||||
item['createdByUser'] as JSONObject,
|
||||
User
|
||||
) as User
|
||||
|
@ -5,6 +5,7 @@ import PageMap from '../../../Utils/PageMap';
|
||||
import RouteMap, { RouteUtil } from '../../../Utils/RouteMap';
|
||||
import PageComponentProps from '../../PageComponentProps';
|
||||
import SideMenu from './SideMenu';
|
||||
import BaseModel from 'Common/Models/BaseModel';
|
||||
import DashboardNavigation from '../../../Utils/Navigation';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
import IncidentPublicNote from 'Model/Models/IncidentPublicNote';
|
||||
@ -17,7 +18,6 @@ import FieldType from 'CommonUI/src/Components/Types/FieldType';
|
||||
import { JSONObject } from 'Common/Types/JSON';
|
||||
import UserElement from '../../../Components/User/User';
|
||||
import User from 'Model/Models/User';
|
||||
import JSONFunctions from 'Common/Types/JSONFunctions';
|
||||
import Navigation from 'CommonUI/src/Utils/Navigation';
|
||||
import AlignItem from 'CommonUI/src/Types/AlignItem';
|
||||
import { ModalWidth } from 'CommonUI/src/Components/Modal/Modal';
|
||||
@ -68,7 +68,7 @@ const PublicNote: FunctionComponent<PageComponentProps> = (
|
||||
|
||||
if (incidentNoteTemplate) {
|
||||
const initialValue: JSONObject = {
|
||||
...JSONFunctions.toJSONObject(
|
||||
...BaseModel.toJSONObject(
|
||||
incidentNoteTemplate,
|
||||
IncidentNoteTemplate
|
||||
),
|
||||
@ -243,7 +243,7 @@ const PublicNote: FunctionComponent<PageComponentProps> = (
|
||||
return (
|
||||
<UserElement
|
||||
user={
|
||||
JSONFunctions.fromJSON(
|
||||
BaseModel.fromJSON(
|
||||
item['createdByUser'] as JSONObject,
|
||||
User
|
||||
) as User
|
||||
|
@ -5,6 +5,7 @@ import PageMap from '../../../Utils/PageMap';
|
||||
import RouteMap, { RouteUtil } from '../../../Utils/RouteMap';
|
||||
import PageComponentProps from '../../PageComponentProps';
|
||||
import SideMenu from './SideMenu';
|
||||
import BaseModel from 'Common/Models/BaseModel';
|
||||
import DashboardNavigation from '../../../Utils/Navigation';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
import Monitor from 'Model/Models/Monitor';
|
||||
@ -17,7 +18,6 @@ import FormFieldSchemaType from 'CommonUI/src/Components/Forms/Types/FormFieldSc
|
||||
import MonitorStatus from 'Model/Models/MonitorStatus';
|
||||
import Incident from 'Model/Models/Incident';
|
||||
import ModelTable from 'CommonUI/src/Components/ModelTable/ModelTable';
|
||||
import JSONFunctions from 'Common/Types/JSONFunctions';
|
||||
import Navigation from 'CommonUI/src/Utils/Navigation';
|
||||
import IncidentSeverity from 'Model/Models/IncidentSeverity';
|
||||
import DisabledWarning from '../../../Components/Monitor/DisabledWarning';
|
||||
@ -272,7 +272,7 @@ const MonitorIncidents: FunctionComponent<PageComponentProps> = (
|
||||
return (
|
||||
<MonitorsElement
|
||||
monitors={
|
||||
JSONFunctions.fromJSON(
|
||||
BaseModel.fromJSON(
|
||||
(item['monitors'] as JSONArray) ||
|
||||
[],
|
||||
Monitor
|
||||
|
@ -26,7 +26,6 @@ import { LIMIT_PER_PROJECT } from 'Common/Types/Database/LimitMax';
|
||||
import SortOrder from 'Common/Types/BaseDatabase/SortOrder';
|
||||
import ModelAPI, { ListResult } from 'CommonUI/src/Utils/ModelAPI/ModelAPI';
|
||||
import MonitorStatusTimeline from 'Model/Models/MonitorStatusTimeline';
|
||||
import JSONFunctions from 'Common/Types/JSONFunctions';
|
||||
import API from 'CommonUI/src/Utils/API/API';
|
||||
import DisabledWarning from '../../../Components/Monitor/DisabledWarning';
|
||||
import MonitorType from 'Common/Types/Monitor/MonitorType';
|
||||
@ -36,6 +35,7 @@ import UptimeUtil from 'CommonUI/src/Components/MonitorGraphs/UptimeUtil';
|
||||
import MonitorStatus from 'Model/Models/MonitorStatus';
|
||||
import { UptimePrecision } from 'Model/Models/StatusPageResource';
|
||||
import ProjectUtil from 'CommonUI/src/Utils/Project';
|
||||
import BaseModel from 'Common/Models/BaseModel';
|
||||
|
||||
const MonitorView: FunctionComponent<PageComponentProps> = (
|
||||
_props: PageComponentProps
|
||||
@ -350,7 +350,7 @@ const MonitorView: FunctionComponent<PageComponentProps> = (
|
||||
return (
|
||||
<LabelsElement
|
||||
labels={
|
||||
JSONFunctions.fromJSON(
|
||||
BaseModel.fromJSON(
|
||||
(item['labels'] as JSONArray) ||
|
||||
[],
|
||||
Label
|
||||
|
@ -12,12 +12,12 @@ import FormFieldSchemaType from 'CommonUI/src/Components/Forms/Types/FormFieldSc
|
||||
import Label from 'Model/Models/Label';
|
||||
import { JSONArray, JSONObject } from 'Common/Types/JSON';
|
||||
import LabelsElement from '../../Components/Label/Labels';
|
||||
import JSONFunctions from 'Common/Types/JSONFunctions';
|
||||
import MonitorGroup from 'Model/Models/MonitorGroup';
|
||||
import Navigation from 'CommonUI/src/Utils/Navigation';
|
||||
import CurrentStatusElement from '../../Components/MonitorGroup/CurrentStatus';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
import BadDataException from 'Common/Types/Exception/BadDataException';
|
||||
import BaseModel from 'Common/Models/BaseModel';
|
||||
|
||||
const MonitorGroupPage: FunctionComponent<PageComponentProps> = (
|
||||
props: PageComponentProps
|
||||
@ -148,7 +148,7 @@ const MonitorGroupPage: FunctionComponent<PageComponentProps> = (
|
||||
return (
|
||||
<LabelsElement
|
||||
labels={
|
||||
JSONFunctions.fromJSON(
|
||||
BaseModel.fromJSON(
|
||||
(item['labels'] as JSONArray) || [],
|
||||
Label
|
||||
) as Array<Label>
|
||||
|
@ -11,10 +11,10 @@ import CardModelDetail from 'CommonUI/src/Components/ModelDetail/CardModelDetail
|
||||
import Navigation from 'CommonUI/src/Utils/Navigation';
|
||||
import Label from 'Model/Models/Label';
|
||||
import { JSONArray, JSONObject } from 'Common/Types/JSON';
|
||||
import BaseModel from 'Common/Models/BaseModel';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
import LabelsElement from '../../../Components/Label/Labels';
|
||||
import MonitorGroup from 'Model/Models/MonitorGroup';
|
||||
import JSONFunctions from 'Common/Types/JSONFunctions';
|
||||
import Card from 'CommonUI/src/Components/Card/Card';
|
||||
import MonitorUptimeGraph from 'CommonUI/src/Components/MonitorGraphs/Uptime';
|
||||
import useAsyncEffect from 'use-async-effect';
|
||||
@ -288,7 +288,7 @@ const MonitorGroupView: FunctionComponent<PageComponentProps> = (
|
||||
return (
|
||||
<LabelsElement
|
||||
labels={
|
||||
JSONFunctions.fromJSON(
|
||||
BaseModel.fromJSON(
|
||||
(item['labels'] as JSONArray) ||
|
||||
[],
|
||||
Label
|
||||
|
@ -15,7 +15,6 @@ import BadDataException from 'Common/Types/Exception/BadDataException';
|
||||
import Monitor from 'Model/Models/Monitor';
|
||||
import { JSONObject } from 'Common/Types/JSON';
|
||||
import MonitorElement from '../../../Components/Monitor/Monitor';
|
||||
import JSONFunctions from 'Common/Types/JSONFunctions';
|
||||
import Navigation from 'CommonUI/src/Utils/Navigation';
|
||||
import MonitorStatus from 'Model/Models/MonitorStatus';
|
||||
import ModelAPI, { ListResult } from 'CommonUI/src/Utils/ModelAPI/ModelAPI';
|
||||
@ -26,6 +25,7 @@ import ErrorMessage from 'CommonUI/src/Components/ErrorMessage/ErrorMessage';
|
||||
import Statusbubble from 'CommonUI/src/Components/StatusBubble/StatusBubble';
|
||||
import Color from 'Common/Types/Color';
|
||||
import MonitorGroup from 'Model/Models/MonitorGroup';
|
||||
import BaseModel from 'Common/Models/BaseModel';
|
||||
|
||||
const MonitorGroupResources: FunctionComponent<PageComponentProps> = (
|
||||
props: PageComponentProps
|
||||
@ -204,7 +204,7 @@ const MonitorGroupResources: FunctionComponent<PageComponentProps> = (
|
||||
return (
|
||||
<MonitorElement
|
||||
monitor={
|
||||
JSONFunctions.fromJSON(
|
||||
BaseModel.fromJSON(
|
||||
(item[
|
||||
'monitor'
|
||||
] as JSONObject) || [],
|
||||
|
@ -11,10 +11,10 @@ import FormFieldSchemaType from 'CommonUI/src/Components/Forms/Types/FormFieldSc
|
||||
import Label from 'Model/Models/Label';
|
||||
import { JSONArray, JSONObject } from 'Common/Types/JSON';
|
||||
import LabelsElement from '../../Components/Label/Labels';
|
||||
import JSONFunctions from 'Common/Types/JSONFunctions';
|
||||
import DashboardNavigation from '../../Utils/Navigation';
|
||||
import Navigation from 'CommonUI/src/Utils/Navigation';
|
||||
import DashboardSideMenu from './SideMenu';
|
||||
import BaseModel from 'Common/Models/BaseModel';
|
||||
|
||||
const OnCallDutyPage: FunctionComponent<PageComponentProps> = (
|
||||
_props: PageComponentProps
|
||||
@ -141,7 +141,7 @@ const OnCallDutyPage: FunctionComponent<PageComponentProps> = (
|
||||
return (
|
||||
<LabelsElement
|
||||
labels={
|
||||
JSONFunctions.fromJSON(
|
||||
BaseModel.fromJSON(
|
||||
(item['labels'] as JSONArray) || [],
|
||||
Label
|
||||
) as Array<Label>
|
||||
|
@ -5,6 +5,7 @@ import PageMap from '../../../Utils/PageMap';
|
||||
import RouteMap, { RouteUtil } from '../../../Utils/RouteMap';
|
||||
import PageComponentProps from '../../PageComponentProps';
|
||||
import SideMenu from './SideMenu';
|
||||
import BaseModel from 'Common/Models/BaseModel';
|
||||
import FieldType from 'CommonUI/src/Components/Types/FieldType';
|
||||
import FormFieldSchemaType from 'CommonUI/src/Components/Forms/Types/FormFieldSchemaType';
|
||||
import CardModelDetail from 'CommonUI/src/Components/ModelDetail/CardModelDetail';
|
||||
@ -14,7 +15,6 @@ import { JSONArray, JSONObject } from 'Common/Types/JSON';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
import LabelsElement from '../../../Components/Label/Labels';
|
||||
import OnCallDutyPolicy from 'Model/Models/OnCallDutyPolicy';
|
||||
import JSONFunctions from 'Common/Types/JSONFunctions';
|
||||
|
||||
const OnCallDutyPolicyView: FunctionComponent<PageComponentProps> = (
|
||||
_props: PageComponentProps
|
||||
@ -143,7 +143,7 @@ const OnCallDutyPolicyView: FunctionComponent<PageComponentProps> = (
|
||||
return (
|
||||
<LabelsElement
|
||||
labels={
|
||||
JSONFunctions.fromJSON(
|
||||
BaseModel.fromJSON(
|
||||
(item['labels'] as JSONArray) ||
|
||||
[],
|
||||
Label
|
||||
|
@ -27,7 +27,6 @@ import Label from 'Model/Models/Label';
|
||||
import LabelsElement from '../../../Components/Label/Labels';
|
||||
import StatusPage from 'Model/Models/StatusPage';
|
||||
import StatusPagesElement from '../../../Components/StatusPage/StatusPagesLabel';
|
||||
import JSONFunctions from 'Common/Types/JSONFunctions';
|
||||
import OneUptimeDate from 'Common/Types/Date';
|
||||
|
||||
const ScheduledMaintenanceView: FunctionComponent<PageComponentProps> = (
|
||||
@ -286,7 +285,7 @@ const ScheduledMaintenanceView: FunctionComponent<PageComponentProps> = (
|
||||
return (
|
||||
<MonitorsElement
|
||||
monitors={
|
||||
JSONFunctions.fromJSON(
|
||||
BaseModel.fromJSON(
|
||||
(item[
|
||||
'monitors'
|
||||
] as JSONArray) || [],
|
||||
@ -310,7 +309,7 @@ const ScheduledMaintenanceView: FunctionComponent<PageComponentProps> = (
|
||||
return (
|
||||
<StatusPagesElement
|
||||
statusPages={
|
||||
JSONFunctions.fromJSON(
|
||||
BaseModel.fromJSON(
|
||||
(item[
|
||||
'statusPages'
|
||||
] as JSONArray) || [],
|
||||
@ -355,7 +354,7 @@ const ScheduledMaintenanceView: FunctionComponent<PageComponentProps> = (
|
||||
return (
|
||||
<LabelsElement
|
||||
labels={
|
||||
JSONFunctions.fromJSON(
|
||||
BaseModel.fromJSON(
|
||||
(item['labels'] as JSONArray) ||
|
||||
[],
|
||||
Label
|
||||
|
@ -17,7 +17,6 @@ import FieldType from 'CommonUI/src/Components/Types/FieldType';
|
||||
import { JSONObject } from 'Common/Types/JSON';
|
||||
import UserElement from '../../../Components/User/User';
|
||||
import User from 'Model/Models/User';
|
||||
import JSONFunctions from 'Common/Types/JSONFunctions';
|
||||
import Navigation from 'CommonUI/src/Utils/Navigation';
|
||||
import AlignItem from 'CommonUI/src/Types/AlignItem';
|
||||
import { ModalWidth } from 'CommonUI/src/Components/Modal/Modal';
|
||||
@ -31,6 +30,7 @@ import DropdownUtil from 'CommonUI/src/Utils/Dropdown';
|
||||
import BasicFormModal from 'CommonUI/src/Components/FormModal/BasicFormModal';
|
||||
import ConfirmModal from 'CommonUI/src/Components/Modal/ConfirmModal';
|
||||
import ScheduledMaintenanceNoteTemplate from 'Model/Models/ScheduledMaintenanceNoteTemplate';
|
||||
import BaseModel from 'Common/Models/BaseModel';
|
||||
|
||||
const ScheduledMaintenanceDelete: FunctionComponent<PageComponentProps> = (
|
||||
props: PageComponentProps
|
||||
@ -72,7 +72,7 @@ const ScheduledMaintenanceDelete: FunctionComponent<PageComponentProps> = (
|
||||
|
||||
if (scheduledMaintenanceNoteTemplate) {
|
||||
const initialValue: JSONObject = {
|
||||
...JSONFunctions.toJSONObject(
|
||||
...BaseModel.toJSONObject(
|
||||
scheduledMaintenanceNoteTemplate,
|
||||
ScheduledMaintenanceNoteTemplate
|
||||
),
|
||||
@ -238,7 +238,7 @@ const ScheduledMaintenanceDelete: FunctionComponent<PageComponentProps> = (
|
||||
return (
|
||||
<UserElement
|
||||
user={
|
||||
JSONFunctions.fromJSON(
|
||||
BaseModel.fromJSON(
|
||||
item['createdByUser'] as JSONObject,
|
||||
User
|
||||
) as User
|
||||
|
@ -5,6 +5,7 @@ import PageMap from '../../../Utils/PageMap';
|
||||
import RouteMap, { RouteUtil } from '../../../Utils/RouteMap';
|
||||
import PageComponentProps from '../../PageComponentProps';
|
||||
import SideMenu from './SideMenu';
|
||||
import BaseModel from 'Common/Models/BaseModel';
|
||||
import DashboardNavigation from '../../../Utils/Navigation';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
import ScheduledMaintenancePublicNote from 'Model/Models/ScheduledMaintenancePublicNote';
|
||||
@ -17,7 +18,6 @@ import FieldType from 'CommonUI/src/Components/Types/FieldType';
|
||||
import { JSONObject } from 'Common/Types/JSON';
|
||||
import UserElement from '../../../Components/User/User';
|
||||
import User from 'Model/Models/User';
|
||||
import JSONFunctions from 'Common/Types/JSONFunctions';
|
||||
import Navigation from 'CommonUI/src/Utils/Navigation';
|
||||
import AlignItem from 'CommonUI/src/Types/AlignItem';
|
||||
import { ModalWidth } from 'CommonUI/src/Components/Modal/Modal';
|
||||
@ -73,7 +73,7 @@ const PublicNote: FunctionComponent<PageComponentProps> = (
|
||||
|
||||
if (scheduledMaintenanceNoteTemplate) {
|
||||
const initialValue: JSONObject = {
|
||||
...JSONFunctions.toJSONObject(
|
||||
...BaseModel.toJSONObject(
|
||||
scheduledMaintenanceNoteTemplate,
|
||||
ScheduledMaintenanceNoteTemplate
|
||||
),
|
||||
@ -252,7 +252,7 @@ const PublicNote: FunctionComponent<PageComponentProps> = (
|
||||
return (
|
||||
<UserElement
|
||||
user={
|
||||
JSONFunctions.fromJSON(
|
||||
BaseModel.fromJSON(
|
||||
item['createdByUser'] as JSONObject,
|
||||
User
|
||||
) as User
|
||||
|
@ -20,8 +20,9 @@ import ModelDelete from 'CommonUI/src/Components/ModelDelete/ModelDelete';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
import LabelsElement from '../../Components/Label/Labels';
|
||||
import BadDataException from 'Common/Types/Exception/BadDataException';
|
||||
import JSONFunctions from 'Common/Types/JSONFunctions';
|
||||
import DashboardNavigation from '../../Utils/Navigation';
|
||||
import BaseModel from 'Common/Models/BaseModel';
|
||||
|
||||
const APIKeyView: FunctionComponent<PageComponentProps> = (
|
||||
props: PageComponentProps
|
||||
): ReactElement => {
|
||||
@ -263,7 +264,7 @@ const APIKeyView: FunctionComponent<PageComponentProps> = (
|
||||
return (
|
||||
<LabelsElement
|
||||
labels={
|
||||
JSONFunctions.fromJSON(
|
||||
BaseModel.fromJSON(
|
||||
(item['labels'] as JSONArray) || [],
|
||||
Label
|
||||
) as Array<Label>
|
||||
|
@ -5,6 +5,7 @@ import PageMap from '../../Utils/PageMap';
|
||||
import RouteMap, { RouteUtil } from '../../Utils/RouteMap';
|
||||
import PageComponentProps from '../PageComponentProps';
|
||||
import DashboardSideMenu from './SideMenu';
|
||||
import BaseModel from 'Common/Models/BaseModel';
|
||||
import Navigation from 'CommonUI/src/Utils/Navigation';
|
||||
import ModelDelete from 'CommonUI/src/Components/ModelDelete/ModelDelete';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
@ -22,7 +23,6 @@ import BadDataException from 'Common/Types/Exception/BadDataException';
|
||||
import Pill from 'CommonUI/src/Components/Pill/Pill';
|
||||
import Color from 'Common/Types/Color';
|
||||
import MonitorsElement from '../../Components/Monitor/Monitors';
|
||||
import JSONFunctions from 'Common/Types/JSONFunctions';
|
||||
import OnCallDutyPoliciesView from '../../Components/OnCallPolicy/OnCallPolicies';
|
||||
import LabelsElement from '../../Components/Label/Labels';
|
||||
import DashboardNavigation from '../../Utils/Navigation';
|
||||
@ -320,7 +320,7 @@ const TeamView: FunctionComponent<PageComponentProps> = (
|
||||
return (
|
||||
<MonitorsElement
|
||||
monitors={
|
||||
JSONFunctions.fromJSON(
|
||||
BaseModel.fromJSON(
|
||||
(item[
|
||||
'monitors'
|
||||
] as JSONArray) || [],
|
||||
@ -344,7 +344,7 @@ const TeamView: FunctionComponent<PageComponentProps> = (
|
||||
return (
|
||||
<OnCallDutyPoliciesView
|
||||
onCallPolicies={
|
||||
JSONFunctions.fromJSON(
|
||||
BaseModel.fromJSON(
|
||||
(item[
|
||||
'onCallDutyPolicies'
|
||||
] as JSONArray) || [],
|
||||
@ -375,7 +375,7 @@ const TeamView: FunctionComponent<PageComponentProps> = (
|
||||
return (
|
||||
<LabelsElement
|
||||
labels={
|
||||
JSONFunctions.fromJSON(
|
||||
BaseModel.fromJSON(
|
||||
(item['labels'] as JSONArray) ||
|
||||
[],
|
||||
Label
|
||||
|
@ -5,6 +5,7 @@ import PageMap from '../../Utils/PageMap';
|
||||
import RouteMap, { RouteUtil } from '../../Utils/RouteMap';
|
||||
import PageComponentProps from '../PageComponentProps';
|
||||
import DashboardSideMenu from './SideMenu';
|
||||
import BaseModel from 'Common/Models/BaseModel';
|
||||
import ModelTable from 'CommonUI/src/Components/ModelTable/ModelTable';
|
||||
import DropdownUtil from 'CommonUI/src/Utils/Dropdown';
|
||||
import FieldType from 'CommonUI/src/Components/Types/FieldType';
|
||||
@ -19,7 +20,6 @@ import Project from 'Model/Models/Project';
|
||||
import Team from 'Model/Models/Team';
|
||||
import { JSONArray, JSONObject } from 'Common/Types/JSON';
|
||||
import TeamsElement from '../../Components/Team/TeamsElement';
|
||||
import JSONFunctions from 'Common/Types/JSONFunctions';
|
||||
import Card from 'CommonUI/src/Components/Card/Card';
|
||||
import Link from 'CommonUI/src/Components/Link/Link';
|
||||
import URL from 'Common/Types/API/URL';
|
||||
@ -276,7 +276,7 @@ const SSOPage: FunctionComponent<PageComponentProps> = (
|
||||
return (
|
||||
<TeamsElement
|
||||
teams={
|
||||
JSONFunctions.fromJSON(
|
||||
BaseModel.fromJSON(
|
||||
(item['teams'] as JSONArray) ||
|
||||
[],
|
||||
Team
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user