add create by

This commit is contained in:
Simon Larsen 2023-10-01 12:46:51 +01:00
parent c06c0f8b38
commit e728501ddb
No known key found for this signature in database
GPG Key ID: AB45983AA9C81CDE
13 changed files with 333 additions and 96 deletions

View File

@ -2,6 +2,7 @@ import TableColumnType from '../Types/BaseDatabase/TableColumnType';
import AnalyticsTableColumn from '../Types/AnalyticsDatabase/TableColumn';
import BadDataException from '../Types/Exception/BadDataException';
import AnalyticsTableEngine from '../Types/AnalyticsDatabase/AnalyticsTableEngine';
import { JSONValue } from '../Types/JSON';
export default class AnalyticsDataModel {
private _tableColumns: Array<AnalyticsTableColumn> = [];
@ -101,4 +102,65 @@ export default class AnalyticsDataModel {
this.primaryKeys = data.primaryKeys;
this.tableColumns = columns;
}
public setColumnValue(
columnName: string,
value: JSONValue
): void {
if (this.getTableColumn(columnName)) {
return ((this as any)[columnName] = value as any);
}
}
public getTableColumn(name: string): AnalyticsTableColumn | null {
const column: AnalyticsTableColumn | undefined = this.tableColumns.find(
(column: AnalyticsTableColumn) => {
return column.key === name;
}
);
if (!column) {
return null;
}
return column;
}
public getTableColumns(): Array<AnalyticsTableColumn> {
return this.tableColumns;
}
public getTenantColumn(): AnalyticsTableColumn | null {
const column: AnalyticsTableColumn | undefined = this.tableColumns.find(
(column: AnalyticsTableColumn) => {
return column.isTenantId;
}
);
if (!column) {
return null;
}
return column;
}
public getRequiredColumns(): Array<AnalyticsTableColumn> {
return this.tableColumns.filter((column: AnalyticsTableColumn) => {
return column.required;
});
}
public isDefaultValueColumn(columnName: string): boolean {
const column: AnalyticsTableColumn | null = this.getTableColumn(
columnName
);
if (!column) {
return false;
}
return column.isDefaultValueColumn;
}
}

View File

@ -33,6 +33,16 @@ export default class AnalyticsTableColumn {
this._required = v;
}
private _isTenantId : boolean = false;
public get isTenantId() : boolean {
return this._isTenantId;
}
public set isTenantId(v : boolean) {
this._isTenantId = v;
}
private _type: TableColumnType = TableColumnType.ShortText;
public get type(): TableColumnType {
return this._type;
@ -40,6 +50,23 @@ export default class AnalyticsTableColumn {
public set type(v: TableColumnType) {
this._type = v;
}
private _forceGetDefaultValueOnCreate?: (() => Date | string | number | boolean) | undefined;
public get forceGetDefaultValueOnCreate(): (() => Date | string | number | boolean) | undefined {
return this._forceGetDefaultValueOnCreate;
}
public set forceGetDefaultValueOnCreate(v: (() => Date | string | number | boolean) | undefined) {
this._forceGetDefaultValueOnCreate = v;
}
private _isDefaultValueColumn : boolean = false;
public get isDefaultValueColumn() : boolean {
return this._isDefaultValueColumn;
}
public set isDefaultValueColumn(v : boolean) {
this._isDefaultValueColumn = v;
}
public constructor(data: {
key: string;
@ -47,11 +74,17 @@ export default class AnalyticsTableColumn {
description: string;
required: boolean;
type: TableColumnType;
isDefaultValueColumn? : boolean | undefined;
isTenantId?: boolean | undefined;
forceGetDefaultValueOnCreate?: (() => Date | string | number | boolean) | undefined;
}) {
this.key = data.key;
this.title = data.title;
this.description = data.description;
this.required = data.required;
this.type = data.type;
this.isTenantId = data.isTenantId || false;
this.forceGetDefaultValueOnCreate = data.forceGetDefaultValueOnCreate;
this.isDefaultValueColumn = data.isDefaultValueColumn || false;
}
}

View File

@ -4,11 +4,14 @@ import ClickhouseDatabase, {
ClickhouseClient,
} from '../Infrastructure/ClickhouseDatabase';
import BaseService from './BaseService';
import AnalyticsBaseModel from 'Common/Models/AnalyticsBaseModel';
import AnalyticsBaseModel from 'Common/AnalyticsModels/BaseModel';
import BadDataException from 'Common/Types/Exception/BadDataException';
import logger from '../Utils/Logger';
import AnalyticsTableColumn from 'Common/Types/AnalyticsDatabase/TableColumn';
// import CreateBy from "../Types/AnalyticsDatabase/CreateBy";
import CreateBy from '../Types/AnalyticsDatabase/CreateBy';
import { OnCreate } from '../Types/AnalyticsDatabase/Hooks';
import Typeof from 'Common/Types/Typeof';
import ModelPermission from '../Utils/ModelPermission';
export default class AnalyticsDatabaseService<
TBaseModel extends AnalyticsBaseModel
@ -59,6 +62,19 @@ export default class AnalyticsDatabaseService<
return statement;
}
protected generateDefaultValues(data: TBaseModel): TBaseModel {
const tableColumns: Array<AnalyticsTableColumn> = data.getTableColumns();
for (const column of tableColumns) {
if (column.forceGetDefaultValueOnCreate) {
data.setColumnValue(column.key, column.forceGetDefaultValueOnCreate());
}
}
return data;
}
public useDefaultDatabase(): void {
this.database = ClickhouseAppInstance;
this.databaseClient = this.database.getDataSource() as ClickhouseClient;
@ -74,99 +90,120 @@ export default class AnalyticsDatabaseService<
});
}
// public async create(createBy: CreateBy<TBaseModel>): Promise<TBaseModel> {
protected async onBeforeCreate(
createBy: CreateBy<TBaseModel>
): Promise<OnCreate<TBaseModel>> {
// A place holder method used for overriding.
return Promise.resolve({
createBy: createBy as CreateBy<TBaseModel>,
carryForward: undefined,
});
}
// const onCreate: OnCreate<TBaseModel> = createBy.props.ignoreHooks
// ? { createBy, carryForward: [] }
// : await this._onBeforeCreate(createBy);
private async _onBeforeCreate(
createBy: CreateBy<TBaseModel>
): Promise<OnCreate<TBaseModel>> {
// Private method that runs before create.
const projectIdColumn: string | null = this.model.getTenantColumn()?.key || null;
// let _createdBy: CreateBy<TBaseModel> = onCreate.createBy;
if (projectIdColumn && createBy.props.tenantId) {
(createBy.data as any)[projectIdColumn] = createBy.props.tenantId;
}
// const carryForward: any = onCreate.carryForward;
return await this.onBeforeCreate(createBy);
}
// _createdBy = this.generateSlug(_createdBy);
public async create(createBy: CreateBy<TBaseModel>): Promise<TBaseModel> {
// let data: TBaseModel = _createdBy.data;
const onCreate: OnCreate<TBaseModel> = createBy.props.ignoreHooks
? { createBy, carryForward: [] }
: await this._onBeforeCreate(createBy);
// // add tenantId if present.
// const tenantColumnName: string | null = data.getTenantColumn();
let _createdBy: CreateBy<TBaseModel> = onCreate.createBy;
// if (tenantColumnName && _createdBy.props.tenantId) {
// data.setColumnValue(tenantColumnName, _createdBy.props.tenantId);
// }
const carryForward: any = onCreate.carryForward;
// data = this.generateDefaultValues(data);
// data = this.checkRequiredFields(data);
let data: TBaseModel = _createdBy.data;
// if (!this.isValid(data)) {
// throw new BadDataException('Data is not valid');
// }
// add tenantId if present.
const tenantColumnName: string | null = data.getTenantColumn()?.key || null;
// // check total items by.
if (tenantColumnName && _createdBy.props.tenantId) {
data.setColumnValue(tenantColumnName, _createdBy.props.tenantId);
}
// await this.checkTotalItemsBy(_createdBy);
data = this.generateDefaultValues(data);
data = this.checkRequiredFields(data);
// // Encrypt data
// data = this.encrypt(data);
if (!this.isValid(data)) {
throw new BadDataException('Data is not valid');
}
// // hash data
// data = await this.hash(data);
// check total items by
// ModelPermission.checkCreatePermissions(
// this.entityType,
// data,
// _createdBy.props
// );
ModelPermission.checkCreatePermissions(
this.entityType,
data,
_createdBy.props
);
// createBy.data = data;
createBy.data = data;
// // check uniqueColumns by:
// createBy = await this.checkUniqueColumnBy(createBy);
// check uniqueColumns by:
createBy = await this.checkUniqueColumnBy(createBy);
// // serialize.
// createBy.data = (await this.sanitizeCreateOrUpdate(
// createBy.data,
// createBy.props
// )) as TBaseModel;
// serialize.
createBy.data = (await this.sanitizeCreateOrUpdate(
createBy.data,
createBy.props
)) as TBaseModel;
// try {
// createBy.data = await this.getRepository().save(createBy.data);
try {
createBy.data = await this.getRepository().save(createBy.data);
// if (!createBy.props.ignoreHooks) {
// createBy.data = await this.onCreateSuccess(
// {
// createBy,
// carryForward,
// },
// createBy.data
// );
// }
if (!createBy.props.ignoreHooks) {
createBy.data = await this.onCreateSuccess(
{
createBy,
carryForward,
},
createBy.data
);
}
// // hit workflow.;
// if (this.getModel().enableWorkflowOn?.create) {
// let tenantId: ObjectID | undefined = createBy.props.tenantId;
// hit workflow.;
if (this.getModel().enableWorkflowOn?.create) {
let tenantId: ObjectID | undefined = createBy.props.tenantId;
// if (!tenantId && this.getModel().getTenantColumn()) {
// tenantId = createBy.data.getValue<ObjectID>(
// this.getModel().getTenantColumn()!
// );
// }
if (!tenantId && this.getModel().getTenantColumn()) {
tenantId = createBy.data.getValue<ObjectID>(
this.getModel().getTenantColumn()!
);
}
// if (tenantId) {
// await this.onTrigger(
// createBy.data.id!,
// tenantId,
// 'on-create'
// );
// }
// }
if (tenantId) {
await this.onTrigger(
createBy.data.id!,
tenantId,
'on-create'
);
}
}
// return createBy.data;
// } catch (error) {
// await this.onCreateError(error as Exception);
// throw this.getException(error as Exception);
// }
// }
return createBy.data;
} catch (error) {
await this.onCreateError(error as Exception);
throw this.getException(error as Exception);
}
}
protected isValid(data: TBaseModel): boolean {
if (!data) {
throw new BadDataException('Data cannot be null');
}
return true;
}
public toColumnsCreateStatement(): string {
let columns: string = '';
@ -180,6 +217,32 @@ export default class AnalyticsDatabaseService<
return columns;
}
protected checkRequiredFields(data: TBaseModel): TBaseModel {
// Check required fields.
for (const columns of data.getRequiredColumns()) {
const requiredField: string = columns.key;
if (typeof (data as any)[requiredField] === Typeof.Boolean) {
if (
!(data as any)[requiredField] &&
(data as any)[requiredField] !== false &&
!data.isDefaultValueColumn(requiredField)
) {
throw new BadDataException(`${requiredField} is required`);
}
} else if (
!(data as any)[requiredField] &&
!data.isDefaultValueColumn(requiredField)
) {
throw new BadDataException(`${requiredField} is required`);
}
}
return data;
}
public toColumnType(type: TableColumnType): string {
if (type === TableColumnType.ShortText) {
return 'String';

View File

@ -53,28 +53,9 @@ import Text from 'Common/Types/Text';
import logger from '../Utils/Logger';
import BaseService from './BaseService';
import { getMaxLengthFromTableColumnType } from 'Common/Types/Database/ColumnLength';
import { OnCreate, OnDelete, OnFind, OnUpdate } from '../Types/Database/Hooks';
export type DatabaseTriggerType = 'on-create' | 'on-update' | 'on-delete';
export interface OnCreate<TBaseModel extends BaseModel> {
createBy: CreateBy<TBaseModel>;
carryForward: any;
}
export interface OnFind<TBaseModel extends BaseModel> {
findBy: FindBy<TBaseModel>;
carryForward: any;
}
export interface OnDelete<TBaseModel extends BaseModel> {
deleteBy: DeleteBy<TBaseModel>;
carryForward: any;
}
export interface OnUpdate<TBaseModel extends BaseModel> {
updateBy: UpdateBy<TBaseModel>;
carryForward: any;
}
class DatabaseService<TBaseModel extends BaseModel> extends BaseService {
private postgresDatabase!: PostgresDatabase;

View File

@ -1,4 +1,4 @@
import AnalyticsBaseModel from 'Common/Models/AnalyticsBaseModel';
import AnalyticsBaseModel from 'Common/AnalyticsModels/BaseModel';
import DatabaseCommonInteractionProps from 'Common/Types/Database/DatabaseCommonInteractionProps';
export default interface CreateBy<TBaseModel extends AnalyticsBaseModel> {

View File

@ -0,0 +1,7 @@
import AnalyticsBaseModel from 'Common/Models/AnalyticsBaseModel';
import DatabaseCommonInteractionProps from 'Common/Types/Database/DatabaseCommonInteractionProps';
export default interface DeleteBy<TBaseModel extends AnalyticsBaseModel> {
data: TBaseModel;
props: DatabaseCommonInteractionProps;
}

View File

@ -0,0 +1,7 @@
import AnalyticsBaseModel from 'Common/Models/AnalyticsBaseModel';
import DatabaseCommonInteractionProps from 'Common/Types/Database/DatabaseCommonInteractionProps';
export default interface FindBy<TBaseModel extends AnalyticsBaseModel> {
data: TBaseModel;
props: DatabaseCommonInteractionProps;
}

View File

@ -0,0 +1,27 @@
import BaseModel from "Common/AnalyticsModels/BaseModel";
import CreateBy from "./CreateBy";
import DeleteBy from "./DeleteBy";
import FindBy from "./FindBy";
import UpdateBy from "./UpdateBy";
export type DatabaseTriggerType = 'on-create' | 'on-update' | 'on-delete';
export interface OnCreate<TBaseModel extends BaseModel> {
createBy: CreateBy<TBaseModel>;
carryForward: any;
}
export interface OnFind<TBaseModel extends BaseModel> {
findBy: FindBy<TBaseModel>;
carryForward: any;
}
export interface OnDelete<TBaseModel extends BaseModel> {
deleteBy: DeleteBy<TBaseModel>;
carryForward: any;
}
export interface OnUpdate<TBaseModel extends BaseModel> {
updateBy: UpdateBy<TBaseModel>;
carryForward: any;
}

View File

@ -0,0 +1,7 @@
import AnalyticsBaseModel from 'Common/Models/AnalyticsBaseModel';
import DatabaseCommonInteractionProps from 'Common/Types/Database/DatabaseCommonInteractionProps';
export default interface UpdateBy<TBaseModel extends AnalyticsBaseModel> {
data: TBaseModel;
props: DatabaseCommonInteractionProps;
}

View File

@ -0,0 +1,27 @@
import BaseModel from "Common/Models/BaseModel";
import CreateBy from "./CreateBy";
import DeleteBy from "./DeleteBy";
import FindBy from "./FindBy";
import UpdateBy from "./UpdateBy";
export type DatabaseTriggerType = 'on-create' | 'on-update' | 'on-delete';
export interface OnCreate<TBaseModel extends BaseModel> {
createBy: CreateBy<TBaseModel>;
carryForward: any;
}
export interface OnFind<TBaseModel extends BaseModel> {
findBy: FindBy<TBaseModel>;
carryForward: any;
}
export interface OnDelete<TBaseModel extends BaseModel> {
deleteBy: DeleteBy<TBaseModel>;
carryForward: any;
}
export interface OnUpdate<TBaseModel extends BaseModel> {
updateBy: UpdateBy<TBaseModel>;
carryForward: any;
}

23
Llama/Dockerfile.tpl Normal file
View File

@ -0,0 +1,23 @@
ARG TAG=latest
FROM continuumio/miniconda3:$TAG
RUN apt-get update \
&& DEBIAN_FRONTEND="noninteractive" apt-get install -y --no-install-recommends \
git \
locales \
sudo \
build-essential \
dpkg-dev \
wget \
openssh-server \
nano \
&& rm -rf /var/lib/apt/lists/*
# Setting up locales
RUN locale-gen en_US.UTF-8
ENV LANG en_US.UTF-8
# Updating conda to the latest version
RUN conda update conda -y

View File

@ -1,4 +1,4 @@
import AnalyticsBaseModel from 'Common/Models/AnalyticsBaseModel';
import AnalyticsBaseModel from 'Common/AnalyticsModels/BaseModel';
import AnalyticsTableColumn from 'Common/Types/AnalyticsDatabase/TableColumn';
import TableColumnType from 'Common/Types/BaseDatabase/TableColumnType';
import AnalyticsTableEngine from 'Common/Types/AnalyticsDatabase/AnalyticsTableEngine';

View File

@ -32,8 +32,8 @@ If you need advanced features, such as API Access, Advances Workflows, or Advanc
## Installation
- [Install on Kubernetes with Helm](https://artifacthub.io/packages/helm/oneuptime/oneuptime) (recommended for production)
- [Install with Docker Compose](https://github.com/OneUptime/oneuptime/blob/master/Docs/Installation/DockerCompose.md) (hobby install, not recommended for production use)
- [Install for local development](https://github.com/OneUptime/oneuptime/blob/master/Docs/Installation/Development.md)
- [Install with Docker Compose](https://github.com/OneUptime/oneuptime/blob/master/Docs/Installation/DockerCompose.md) (hobby install, not recommended for production)
- [Install for Local Development](https://github.com/OneUptime/oneuptime/blob/master/Docs/Installation/Development.md)
## Philosophy