refactor base api

This commit is contained in:
Simon Larsen
2023-11-13 19:31:44 +00:00
parent 2f6f81385f
commit 5128e17205
17 changed files with 675 additions and 73 deletions

View File

@@ -8,14 +8,17 @@ 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';
export default class AnalyticsDataModel 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;
@@ -100,6 +103,7 @@ export default class AnalyticsDataModel extends CommonModel {
data.allowAccessIfSubscriptionIsUnpaid || false;
this.accessControl = data.accessControl;
this.enableWorkflowOn = data.enableWorkflowOn;
this.crudApiPath = data.crudApiPath;
// initialize Arrays.
for (const column of this.tableColumns) {
@@ -185,6 +189,17 @@ 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) => {

View File

@@ -8,6 +8,7 @@ import TableColumnType from './Database/TableColumnType';
import SerializableObject from './SerializableObject';
import SerializableObjectDictionary from './SerializableObjectDictionary';
import JSON5 from 'json5';
import AnalyticsDataModel from '../AnalyticsModels/BaseModel';
export default class JSONFunctions {
public static isEmptyObject(
@@ -21,20 +22,20 @@ export default class JSONFunctions {
}
public static toJSON(
model: BaseModel,
modelType: { new (): BaseModel }
model: BaseModel | AnalyticsDataModel,
modelType: { new (): BaseModel | AnalyticsDataModel }
): JSONObject {
const json: JSONObject = this.toJSONObject(model, modelType);
return JSONFunctions.serialize(json);
}
public static toJSONObject(
model: BaseModel,
modelType: { new (): BaseModel }
model: BaseModel | AnalyticsDataModel,
modelType: { new (): BaseModel | AnalyticsDataModel }
): JSONObject {
const json: JSONObject = {};
const vanillaModel: BaseModel = new modelType();
const vanillaModel: BaseModel | AnalyticsDataModel = new modelType();
for (const key of vanillaModel.getTableColumns().columns) {
if ((model as any)[key] === undefined) {
@@ -89,8 +90,8 @@ export default class JSONFunctions {
}
public static toJSONArray(
list: Array<BaseModel>,
modelType: { new (): BaseModel }
list: Array<BaseModel | AnalyticsDataModel>,
modelType: { new (): BaseModel | AnalyticsDataModel }
): JSONArray {
const array: JSONArray = [];

View File

@@ -29,6 +29,7 @@ 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 +193,7 @@ export default class BaseAPI<
const permissions: Array<UserPermission> = [];
const props: DatabaseCommonInteractionProps =
await this.getDatabaseCommonInteractionProps(req);
await CommonAPI.getDatabaseCommonInteractionProps(req);
if (
props &&
@@ -217,63 +218,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 +257,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 +297,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 +326,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 +343,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 +371,7 @@ export default class BaseAPI<
_id: objectIdString,
},
data: item,
props: await this.getDatabaseCommonInteractionProps(req),
props: await CommonAPI.getDatabaseCommonInteractionProps(req),
});
return Response.sendEmptyResponse(req, res);
@@ -452,7 +396,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);

View File

@@ -0,0 +1,457 @@
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: PartialEntity<TAnalyticsDataModel> = JSONFunctions.deserialize(
body['data'] as JSONObject
) as PartialEntity<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 = JSONFunctions.fromJSON<TAnalyticsDataModel>(
body['data'] as JSONObject,
this.entityType
) as TAnalyticsDataModel;
const miscDataProps: JSONObject = JSONFunctions.deserialize(
body['miscDataProps'] as JSONObject
);
const createBy: CreateBy<TAnalyticsDataModel> = {
data: item,
miscDataProps: miscDataProps,
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);
}
}

View 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;
}
}

View File

@@ -18,6 +18,7 @@ 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,7 +175,7 @@ export default class Response {
public static sendEntityArrayResponse(
req: ExpressRequest,
res: ExpressResponse,
list: Array<BaseModel>,
list: Array<BaseModel | AnalyticsDataModel>,
count: PositiveNumber | number,
modelType: { new (): BaseModel }
): void {
@@ -186,7 +187,7 @@ export default class Response {
req,
res,
JSONFunctions.serializeArray(
JSONFunctions.toJSONArray(list as Array<BaseModel>, modelType)
JSONFunctions.toJSONArray(list as Array<BaseModel | AnalyticsDataModel>, modelType)
),
count
);

View File

@@ -0,0 +1,31 @@
import React, { FunctionComponent, ReactElement, useEffect } from 'react';
import Log from 'Model/AnalyticsModels/Log';
export interface ComponentProps {
log: Log
}
const LogItem: FunctionComponent<ComponentProps> = (
props: ComponentProps
): ReactElement => {
const [isCollapsed, setIsCollapsed] = React.useState<boolean>(true);
useEffect(() => {
setIsCollapsed(true);
}, [])
if(isCollapsed){
return (<div className='color-gray-100 flex'>
{/* Collapsable icon when clicked should expand */}
</div>)
}
return (
<div className='color-gray-100'>
</div>
);
};
export default LogItem;

View File

@@ -0,0 +1,17 @@
import React, { FunctionComponent, ReactElement } from 'react';
export interface ComponentProps {
}
const LogsFilter: FunctionComponent<ComponentProps> = (
props: ComponentProps
): ReactElement => {
return (
<div>
</div>
);
};
export default LogsFilter;

View File

@@ -0,0 +1,21 @@
import Log from 'Model/AnalyticsModels/Log';
import React, { FunctionComponent, ReactElement } from 'react';
import LogItem from './LogItem';
export interface ComponentProps {
logs: Array<Log>
}
const LogsViewer: FunctionComponent<ComponentProps> = (
props: ComponentProps
): ReactElement => {
return (
<div className='shadow rounded bg-slate-600 p-2'>
{props.logs.map((log: Log) => {
return <LogItem log={log} />;
})}
</div>
);
};
export default LogsViewer;

View File

@@ -0,0 +1,24 @@
import ObjectID from 'Common/Types/ObjectID';
import React, { FunctionComponent, ReactElement } from 'react';
export interface ComponentProps {
telemetryServiceIds: Array<ObjectID>;
}
const LabelsElement: FunctionComponent<ComponentProps> = (
props: ComponentProps
): ReactElement => {
return (
<div>
{props.labels.map((label: Label, i: number) => {
return <LabelElement label={label} key={i} />;
})}
</div>
);
};
export default LabelsElement;

View File

@@ -14,6 +14,7 @@ const ServiceDelete: FunctionComponent<PageComponentProps> = (
): ReactElement => {
const modelId: ObjectID = Navigation.getLastParamAsObjectID(1);
return (
<ModelPage
title="Service"

View File

@@ -4,6 +4,7 @@ import TableColumnType from 'Common/Types/AnalyticsDatabase/TableColumnType';
import AnalyticsTableEngine from 'Common/Types/AnalyticsDatabase/AnalyticsTableEngine';
import ObjectID from 'Common/Types/ObjectID';
import KeyValueNestedModel from './NestedModels/KeyValueNestedModel';
import Route from 'Common/Types/API/Route';
export default class Log extends AnalyticsBaseModel {
public constructor() {
@@ -12,6 +13,7 @@ export default class Log extends AnalyticsBaseModel {
tableEngine: AnalyticsTableEngine.MergeTree,
singularName: 'Log',
pluralName: 'Logs',
crudApiPath: new Route('/logs'),
tableColumns: [
new AnalyticsTableColumn({
key: 'projectId',

View File

@@ -4,6 +4,7 @@ import TableColumnType from 'Common/Types/AnalyticsDatabase/TableColumnType';
import AnalyticsTableEngine from 'Common/Types/AnalyticsDatabase/AnalyticsTableEngine';
import ObjectID from 'Common/Types/ObjectID';
import KeyValueNestedModel from './NestedModels/KeyValueNestedModel';
import Route from 'Common/Types/API/Route';
export default class Metric extends AnalyticsBaseModel {
public constructor() {
@@ -11,6 +12,7 @@ export default class Metric extends AnalyticsBaseModel {
tableName: 'MetricGauge',
tableEngine: AnalyticsTableEngine.MergeTree,
singularName: 'Metric Gauge',
crudApiPath: new Route('/metrics/gauge'),
pluralName: 'Metrics Gauge',
tableColumns: [
new AnalyticsTableColumn({

View File

@@ -4,6 +4,7 @@ import TableColumnType from 'Common/Types/AnalyticsDatabase/TableColumnType';
import AnalyticsTableEngine from 'Common/Types/AnalyticsDatabase/AnalyticsTableEngine';
import ObjectID from 'Common/Types/ObjectID';
import KeyValueNestedModel from './NestedModels/KeyValueNestedModel';
import Route from 'Common/Types/API/Route';
export default class Metric extends AnalyticsBaseModel {
public constructor() {
@@ -12,6 +13,7 @@ export default class Metric extends AnalyticsBaseModel {
tableEngine: AnalyticsTableEngine.MergeTree,
singularName: 'Metric Histogram',
pluralName: 'Metrics Histogram',
crudApiPath: new Route('/metrics/histogram'),
tableColumns: [
new AnalyticsTableColumn({
key: 'projectId',

View File

@@ -4,6 +4,7 @@ import TableColumnType from 'Common/Types/AnalyticsDatabase/TableColumnType';
import AnalyticsTableEngine from 'Common/Types/AnalyticsDatabase/AnalyticsTableEngine';
import ObjectID from 'Common/Types/ObjectID';
import KeyValueNestedModel from './NestedModels/KeyValueNestedModel';
import Route from 'Common/Types/API/Route';
export default class Metric extends AnalyticsBaseModel {
public constructor() {
@@ -11,6 +12,7 @@ export default class Metric extends AnalyticsBaseModel {
tableName: 'MetricSum',
tableEngine: AnalyticsTableEngine.MergeTree,
singularName: 'Metric Sum',
crudApiPath: new Route('/metrics/sum'),
pluralName: 'Metrics Sum',
tableColumns: [
new AnalyticsTableColumn({

View File

@@ -5,6 +5,7 @@ import AnalyticsTableEngine from 'Common/Types/AnalyticsDatabase/AnalyticsTableE
import ObjectID from 'Common/Types/ObjectID';
import KeyValueNestedModel from './NestedModels/KeyValueNestedModel';
import NestedModel from 'Common/AnalyticsModels/NestedModel';
import Route from 'Common/Types/API/Route';
export class SpanEvent extends NestedModel {
public constructor() {
@@ -113,6 +114,7 @@ export default class Span extends AnalyticsBaseModel {
tableEngine: AnalyticsTableEngine.MergeTree,
singularName: 'Span',
pluralName: 'Spans',
crudApiPath: new Route('/span'),
tableColumns: [
new AnalyticsTableColumn({
key: 'projectId',

View File

@@ -479,6 +479,21 @@ server {
client_max_body_size 50M;
}
location /analytics-api {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# enable WebSockets (for ws://sockjs not connected error in the accounts source: https://stackoverflow.com/questions/41381444/websocket-connection-failed-error-during-websocket-handshake-unexpected-respon)
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_pass http://dashboard-api;
client_max_body_size 50M;
}
location /heartbeat {
proxy_set_header Host $host;