feat: Convert model files to JS
This commit is contained in:
@@ -1,19 +1,9 @@
|
||||
import { IJSONObject } from '@automatisch/types';
|
||||
import { AES, enc } from 'crypto-js';
|
||||
import { ModelOptions, QueryContext } from 'objection';
|
||||
import appConfig from '../config/app';
|
||||
import AppConfig from './app-config';
|
||||
import Base from './base';
|
||||
|
||||
class AppAuthClient extends Base {
|
||||
id!: string;
|
||||
name: string;
|
||||
active: boolean;
|
||||
appConfigId!: string;
|
||||
authDefaults: string;
|
||||
formattedAuthDefaults?: IJSONObject;
|
||||
appConfig?: AppConfig;
|
||||
|
||||
static tableName = 'app_auth_clients';
|
||||
|
||||
static jsonSchema = {
|
||||
@@ -42,7 +32,7 @@ class AppAuthClient extends Base {
|
||||
},
|
||||
});
|
||||
|
||||
encryptData(): void {
|
||||
encryptData() {
|
||||
if (!this.eligibleForEncryption()) return;
|
||||
|
||||
this.authDefaults = AES.encrypt(
|
||||
@@ -52,7 +42,7 @@ class AppAuthClient extends Base {
|
||||
|
||||
delete this.formattedAuthDefaults;
|
||||
}
|
||||
decryptData(): void {
|
||||
decryptData() {
|
||||
if (!this.eligibleForDecryption()) return;
|
||||
|
||||
this.formattedAuthDefaults = JSON.parse(
|
||||
@@ -60,30 +50,27 @@ class AppAuthClient extends Base {
|
||||
);
|
||||
}
|
||||
|
||||
eligibleForEncryption(): boolean {
|
||||
eligibleForEncryption() {
|
||||
return this.formattedAuthDefaults ? true : false;
|
||||
}
|
||||
|
||||
eligibleForDecryption(): boolean {
|
||||
eligibleForDecryption() {
|
||||
return this.authDefaults ? true : false;
|
||||
}
|
||||
|
||||
// TODO: Make another abstraction like beforeSave instead of using
|
||||
// beforeInsert and beforeUpdate separately for the same operation.
|
||||
async $beforeInsert(queryContext: QueryContext): Promise<void> {
|
||||
async $beforeInsert(queryContext) {
|
||||
await super.$beforeInsert(queryContext);
|
||||
this.encryptData();
|
||||
}
|
||||
|
||||
async $beforeUpdate(
|
||||
opt: ModelOptions,
|
||||
queryContext: QueryContext
|
||||
): Promise<void> {
|
||||
async $beforeUpdate(opt, queryContext) {
|
||||
await super.$beforeUpdate(opt, queryContext);
|
||||
this.encryptData();
|
||||
}
|
||||
|
||||
async $afterFind(): Promise<void> {
|
||||
async $afterFind() {
|
||||
this.decryptData();
|
||||
}
|
||||
}
|
@@ -3,14 +3,6 @@ import Base from './base';
|
||||
import AppAuthClient from './app-auth-client';
|
||||
|
||||
class AppConfig extends Base {
|
||||
id!: string;
|
||||
key!: string;
|
||||
allowCustomConnection: boolean;
|
||||
shared: boolean;
|
||||
disabled: boolean;
|
||||
app?: App;
|
||||
appAuthClients?: AppAuthClient[];
|
||||
|
||||
static tableName = 'app_configs';
|
||||
|
||||
static jsonSchema = {
|
||||
@@ -46,16 +38,13 @@ class AppConfig extends Base {
|
||||
}
|
||||
|
||||
get canConnect() {
|
||||
const hasSomeActiveAppAuthClients = !!this.appAuthClients
|
||||
?.some(appAuthClient => appAuthClient.active);
|
||||
const hasSomeActiveAppAuthClients = !!this.appAuthClients?.some(
|
||||
(appAuthClient) => appAuthClient.active
|
||||
);
|
||||
const shared = this.shared;
|
||||
const active = this.disabled === false;
|
||||
|
||||
const conditions = [
|
||||
hasSomeActiveAppAuthClients,
|
||||
shared,
|
||||
active
|
||||
];
|
||||
const conditions = [hasSomeActiveAppAuthClients, shared, active];
|
||||
|
||||
return conditions.every(Boolean);
|
||||
}
|
@@ -1,6 +1,5 @@
|
||||
import fs from 'fs';
|
||||
import { join } from 'path';
|
||||
import { IApp } from '@automatisch/types';
|
||||
import appInfoConverter from '../helpers/app-info-converter';
|
||||
import getApp from '../helpers/get-app';
|
||||
|
||||
@@ -10,7 +9,7 @@ class App {
|
||||
.readdirSync(this.folderPath)
|
||||
.filter((file) => fs.statSync(this.folderPath + '/' + file).isDirectory());
|
||||
|
||||
static async findAll(name?: string, stripFuncs = true): Promise<IApp[]> {
|
||||
static async findAll(name, stripFuncs = true) {
|
||||
if (!name)
|
||||
return Promise.all(
|
||||
this.list.map(
|
||||
@@ -25,39 +24,45 @@ class App {
|
||||
);
|
||||
}
|
||||
|
||||
static async findOneByName(name: string, stripFuncs = false): Promise<IApp> {
|
||||
static async findOneByName(name, stripFuncs = false) {
|
||||
const rawAppData = await getApp(name.toLocaleLowerCase(), stripFuncs);
|
||||
|
||||
return appInfoConverter(rawAppData);
|
||||
}
|
||||
|
||||
static async findOneByKey(key: string, stripFuncs = false): Promise<IApp> {
|
||||
static async findOneByKey(key, stripFuncs = false) {
|
||||
const rawAppData = await getApp(key, stripFuncs);
|
||||
|
||||
return appInfoConverter(rawAppData);
|
||||
}
|
||||
|
||||
static async checkAppAndAction(appKey: string, actionKey: string): Promise<void> {
|
||||
static async checkAppAndAction(appKey, actionKey) {
|
||||
const app = await this.findOneByKey(appKey);
|
||||
|
||||
if (!actionKey) return;
|
||||
|
||||
const hasAction = app.actions?.find(action => action.key === actionKey);
|
||||
const hasAction = app.actions?.find((action) => action.key === actionKey);
|
||||
|
||||
if (!hasAction) {
|
||||
throw new Error(`${app.name} does not have an action with the "${actionKey}" key!`);
|
||||
throw new Error(
|
||||
`${app.name} does not have an action with the "${actionKey}" key!`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static async checkAppAndTrigger(appKey: string, triggerKey: string): Promise<void> {
|
||||
static async checkAppAndTrigger(appKey, triggerKey) {
|
||||
const app = await this.findOneByKey(appKey);
|
||||
|
||||
if (!triggerKey) return;
|
||||
|
||||
const hasTrigger = app.triggers?.find(trigger => trigger.key === triggerKey);
|
||||
const hasTrigger = app.triggers?.find(
|
||||
(trigger) => trigger.key === triggerKey
|
||||
);
|
||||
|
||||
if (!hasTrigger) {
|
||||
throw new Error(`${app.name} does not have a trigger with the "${triggerKey}" key!`);
|
||||
throw new Error(
|
||||
`${app.name} does not have a trigger with the "${triggerKey}" key!`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,18 +1,12 @@
|
||||
import { AjvValidator, Model, snakeCaseMappers } from 'objection';
|
||||
import type { QueryContext, ModelOptions, ColumnNameMappers } from 'objection';
|
||||
import addFormats from 'ajv-formats';
|
||||
|
||||
import ExtendedQueryBuilder from './query-builder';
|
||||
|
||||
class Base extends Model {
|
||||
createdAt!: string;
|
||||
updatedAt!: string;
|
||||
deletedAt: string;
|
||||
|
||||
QueryBuilderType!: ExtendedQueryBuilder<this>;
|
||||
static QueryBuilder = ExtendedQueryBuilder;
|
||||
|
||||
static get columnNameMappers(): ColumnNameMappers {
|
||||
static get columnNameMappers() {
|
||||
return snakeCaseMappers();
|
||||
}
|
||||
|
||||
@@ -29,17 +23,14 @@ class Base extends Model {
|
||||
});
|
||||
}
|
||||
|
||||
async $beforeInsert(queryContext: QueryContext): Promise<void> {
|
||||
async $beforeInsert(queryContext) {
|
||||
await super.$beforeInsert(queryContext);
|
||||
|
||||
this.createdAt = new Date().toISOString();
|
||||
this.updatedAt = new Date().toISOString();
|
||||
}
|
||||
|
||||
async $beforeUpdate(
|
||||
opts: ModelOptions,
|
||||
queryContext: QueryContext
|
||||
): Promise<void> {
|
||||
async $beforeUpdate(opts, queryContext) {
|
||||
this.updatedAt = new Date().toISOString();
|
||||
|
||||
await super.$beforeUpdate(opts, queryContext);
|
@@ -1,11 +1,6 @@
|
||||
import { IJSONValue } from '@automatisch/types';
|
||||
import Base from './base';
|
||||
|
||||
class Config extends Base {
|
||||
id!: string;
|
||||
key!: string;
|
||||
value!: { data: IJSONValue };
|
||||
|
||||
static tableName = 'config';
|
||||
|
||||
static jsonSchema = {
|
@@ -1,36 +1,15 @@
|
||||
import { QueryContext, ModelOptions } from 'objection';
|
||||
import type { RelationMappings } from 'objection';
|
||||
import { AES, enc } from 'crypto-js';
|
||||
import { IRequest } from '@automatisch/types';
|
||||
import App from './app';
|
||||
import AppConfig from './app-config';
|
||||
import AppAuthClient from './app-auth-client';
|
||||
import Base from './base';
|
||||
import User from './user';
|
||||
import Step from './step';
|
||||
import ExtendedQueryBuilder from './query-builder';
|
||||
import appConfig from '../config/app';
|
||||
import { IJSONObject } from '@automatisch/types';
|
||||
import Telemetry from '../helpers/telemetry';
|
||||
import globalVariable from '../helpers/global-variable';
|
||||
|
||||
class Connection extends Base {
|
||||
id!: string;
|
||||
key!: string;
|
||||
data: string;
|
||||
formattedData?: IJSONObject;
|
||||
userId!: string;
|
||||
verified: boolean;
|
||||
draft: boolean;
|
||||
count?: number;
|
||||
flowCount?: number;
|
||||
user?: User;
|
||||
steps?: Step[];
|
||||
triggerSteps?: Step[];
|
||||
appAuthClientId?: string;
|
||||
appAuthClient?: AppAuthClient;
|
||||
appConfig?: AppConfig;
|
||||
|
||||
static tableName = 'connections';
|
||||
|
||||
static jsonSchema = {
|
||||
@@ -56,7 +35,7 @@ class Connection extends Base {
|
||||
return ['reconnectable'];
|
||||
}
|
||||
|
||||
static relationMappings = (): RelationMappings => ({
|
||||
static relationMappings = () => ({
|
||||
user: {
|
||||
relation: Base.BelongsToOneRelation,
|
||||
modelClass: User,
|
||||
@@ -80,7 +59,7 @@ class Connection extends Base {
|
||||
from: 'connections.id',
|
||||
to: 'steps.connection_id',
|
||||
},
|
||||
filter(builder: ExtendedQueryBuilder<Step>) {
|
||||
filter(builder) {
|
||||
builder.where('type', '=', 'trigger');
|
||||
},
|
||||
},
|
||||
@@ -114,7 +93,7 @@ class Connection extends Base {
|
||||
return true;
|
||||
}
|
||||
|
||||
encryptData(): void {
|
||||
encryptData() {
|
||||
if (!this.eligibleForEncryption()) return;
|
||||
|
||||
this.data = AES.encrypt(
|
||||
@@ -125,7 +104,7 @@ class Connection extends Base {
|
||||
delete this.formattedData;
|
||||
}
|
||||
|
||||
decryptData(): void {
|
||||
decryptData() {
|
||||
if (!this.eligibleForDecryption()) return;
|
||||
|
||||
this.formattedData = JSON.parse(
|
||||
@@ -133,39 +112,36 @@ class Connection extends Base {
|
||||
);
|
||||
}
|
||||
|
||||
eligibleForEncryption(): boolean {
|
||||
eligibleForEncryption() {
|
||||
return this.formattedData ? true : false;
|
||||
}
|
||||
|
||||
eligibleForDecryption(): boolean {
|
||||
eligibleForDecryption() {
|
||||
return this.data ? true : false;
|
||||
}
|
||||
|
||||
// TODO: Make another abstraction like beforeSave instead of using
|
||||
// beforeInsert and beforeUpdate separately for the same operation.
|
||||
async $beforeInsert(queryContext: QueryContext): Promise<void> {
|
||||
async $beforeInsert(queryContext) {
|
||||
await super.$beforeInsert(queryContext);
|
||||
this.encryptData();
|
||||
}
|
||||
|
||||
async $beforeUpdate(
|
||||
opt: ModelOptions,
|
||||
queryContext: QueryContext
|
||||
): Promise<void> {
|
||||
async $beforeUpdate(opt, queryContext) {
|
||||
await super.$beforeUpdate(opt, queryContext);
|
||||
this.encryptData();
|
||||
}
|
||||
|
||||
async $afterFind(): Promise<void> {
|
||||
async $afterFind() {
|
||||
this.decryptData();
|
||||
}
|
||||
|
||||
async $afterInsert(queryContext: QueryContext) {
|
||||
async $afterInsert(queryContext) {
|
||||
await super.$afterInsert(queryContext);
|
||||
Telemetry.connectionCreated(this);
|
||||
}
|
||||
|
||||
async $afterUpdate(opt: ModelOptions, queryContext: QueryContext) {
|
||||
async $afterUpdate(opt, queryContext) {
|
||||
await super.$afterUpdate(opt, queryContext);
|
||||
Telemetry.connectionUpdated(this);
|
||||
}
|
||||
@@ -176,7 +152,7 @@ class Connection extends Base {
|
||||
return await App.findOneByKey(this.key);
|
||||
}
|
||||
|
||||
async verifyWebhook(request: IRequest) {
|
||||
async verifyWebhook(request) {
|
||||
if (!this.key) return true;
|
||||
|
||||
const app = await this.getApp();
|
@@ -1,5 +1,3 @@
|
||||
import type { QueryContext } from 'objection';
|
||||
import { IJSONObject } from '@automatisch/types';
|
||||
import appConfig from '../config/app';
|
||||
import Base from './base';
|
||||
import Execution from './execution';
|
||||
@@ -7,17 +5,6 @@ import Step from './step';
|
||||
import Telemetry from '../helpers/telemetry';
|
||||
|
||||
class ExecutionStep extends Base {
|
||||
id!: string;
|
||||
executionId!: string;
|
||||
stepId!: string;
|
||||
dataIn!: IJSONObject;
|
||||
dataOut!: IJSONObject;
|
||||
errorDetails: IJSONObject;
|
||||
status: 'success' | 'failure';
|
||||
step: Step;
|
||||
execution?: Execution;
|
||||
count?: number;
|
||||
|
||||
static tableName = 'execution_steps';
|
||||
|
||||
static jsonSchema = {
|
||||
@@ -60,7 +47,7 @@ class ExecutionStep extends Base {
|
||||
return this.status === 'failure';
|
||||
}
|
||||
|
||||
async $afterInsert(queryContext: QueryContext) {
|
||||
async $afterInsert(queryContext) {
|
||||
await super.$afterInsert(queryContext);
|
||||
Telemetry.executionStepCreated(this);
|
||||
|
@@ -1,17 +1,9 @@
|
||||
import type { QueryContext } from 'objection';
|
||||
import Base from './base';
|
||||
import Flow from './flow';
|
||||
import ExecutionStep from './execution-step';
|
||||
import Telemetry from '../helpers/telemetry';
|
||||
|
||||
class Execution extends Base {
|
||||
id!: string;
|
||||
flowId!: string;
|
||||
testRun: boolean;
|
||||
internalId: string;
|
||||
executionSteps: ExecutionStep[];
|
||||
flow?: Flow;
|
||||
|
||||
static tableName = 'executions';
|
||||
|
||||
static jsonSchema = {
|
||||
@@ -47,7 +39,7 @@ class Execution extends Base {
|
||||
},
|
||||
});
|
||||
|
||||
async $afterInsert(queryContext: QueryContext) {
|
||||
async $afterInsert(queryContext) {
|
||||
await super.$afterInsert(queryContext);
|
||||
Telemetry.executionCreated(this);
|
||||
}
|
@@ -1,10 +1,4 @@
|
||||
import { ValidationError } from 'objection';
|
||||
import type {
|
||||
ModelOptions,
|
||||
QueryContext,
|
||||
StaticHookArguments,
|
||||
} from 'objection';
|
||||
import ExtendedQueryBuilder from './query-builder';
|
||||
import Base from './base';
|
||||
import Step from './step';
|
||||
import User from './user';
|
||||
@@ -12,19 +6,6 @@ import Execution from './execution';
|
||||
import Telemetry from '../helpers/telemetry';
|
||||
|
||||
class Flow extends Base {
|
||||
id!: string;
|
||||
name!: string;
|
||||
userId!: string;
|
||||
active: boolean;
|
||||
status: 'paused' | 'published' | 'draft';
|
||||
steps: Step[];
|
||||
triggerStep: Step;
|
||||
publishedAt: string;
|
||||
remoteWebhookId: string;
|
||||
executions?: Execution[];
|
||||
lastExecution?: Execution;
|
||||
user?: User;
|
||||
|
||||
static tableName = 'flows';
|
||||
|
||||
static jsonSchema = {
|
||||
@@ -52,7 +33,7 @@ class Flow extends Base {
|
||||
from: 'flows.id',
|
||||
to: 'steps.flow_id',
|
||||
},
|
||||
filter(builder: ExtendedQueryBuilder<Step>) {
|
||||
filter(builder) {
|
||||
builder.orderBy('position', 'asc');
|
||||
},
|
||||
},
|
||||
@@ -63,11 +44,8 @@ class Flow extends Base {
|
||||
from: 'flows.id',
|
||||
to: 'steps.flow_id',
|
||||
},
|
||||
filter(builder: ExtendedQueryBuilder<Step>) {
|
||||
builder
|
||||
.where('type', 'trigger')
|
||||
.limit(1)
|
||||
.first();
|
||||
filter(builder) {
|
||||
builder.where('type', 'trigger').limit(1).first();
|
||||
},
|
||||
},
|
||||
executions: {
|
||||
@@ -85,7 +63,7 @@ class Flow extends Base {
|
||||
from: 'flows.id',
|
||||
to: 'executions.flow_id',
|
||||
},
|
||||
filter(builder: ExtendedQueryBuilder<Execution>) {
|
||||
filter(builder) {
|
||||
builder.orderBy('created_at', 'desc').limit(1).first();
|
||||
},
|
||||
},
|
||||
@@ -99,7 +77,7 @@ class Flow extends Base {
|
||||
},
|
||||
});
|
||||
|
||||
static async afterFind(args: StaticHookArguments<any>): Promise<any> {
|
||||
static async afterFind(args) {
|
||||
const { result } = args;
|
||||
|
||||
const referenceFlow = result[0];
|
||||
@@ -122,7 +100,7 @@ class Flow extends Base {
|
||||
async lastInternalId() {
|
||||
const lastExecution = await this.$relatedQuery('lastExecution');
|
||||
|
||||
return lastExecution ? (lastExecution as Execution).internalId : null;
|
||||
return lastExecution ? lastExecution.internalId : null;
|
||||
}
|
||||
|
||||
async lastInternalIds(itemCount = 50) {
|
||||
@@ -134,15 +112,12 @@ class Flow extends Base {
|
||||
return lastExecutions.map((execution) => execution.internalId);
|
||||
}
|
||||
|
||||
async $beforeUpdate(
|
||||
opt: ModelOptions,
|
||||
queryContext: QueryContext
|
||||
): Promise<void> {
|
||||
async $beforeUpdate(opt, queryContext) {
|
||||
await super.$beforeUpdate(opt, queryContext);
|
||||
|
||||
if (!this.active) return;
|
||||
|
||||
const oldFlow = opt.old as Flow;
|
||||
const oldFlow = opt.old;
|
||||
|
||||
const incompleteStep = await oldFlow.$relatedQuery('steps').findOne({
|
||||
status: 'incomplete',
|
||||
@@ -168,17 +143,17 @@ class Flow extends Base {
|
||||
return;
|
||||
}
|
||||
|
||||
async $afterInsert(queryContext: QueryContext) {
|
||||
async $afterInsert(queryContext) {
|
||||
await super.$afterInsert(queryContext);
|
||||
Telemetry.flowCreated(this);
|
||||
}
|
||||
|
||||
async $afterUpdate(opt: ModelOptions, queryContext: QueryContext) {
|
||||
async $afterUpdate(opt, queryContext) {
|
||||
await super.$afterUpdate(opt, queryContext);
|
||||
Telemetry.flowUpdated(this);
|
||||
}
|
||||
|
||||
async getTriggerStep(): Promise<Step> {
|
||||
async getTriggerStep() {
|
||||
return await this.$relatedQuery('steps').findOne({
|
||||
type: 'trigger',
|
||||
});
|
@@ -3,22 +3,11 @@ import SamlAuthProvider from './saml-auth-provider.ee';
|
||||
import User from './user';
|
||||
|
||||
class Identity extends Base {
|
||||
id!: string;
|
||||
remoteId!: string;
|
||||
userId!: string;
|
||||
providerId!: string;
|
||||
providerType!: 'saml';
|
||||
|
||||
static tableName = 'identities';
|
||||
|
||||
static jsonSchema = {
|
||||
type: 'object',
|
||||
required: [
|
||||
'providerId',
|
||||
'remoteId',
|
||||
'userId',
|
||||
'providerType',
|
||||
],
|
||||
required: ['providerId', 'remoteId', 'userId', 'providerType'],
|
||||
|
||||
properties: {
|
||||
id: { type: 'string', format: 'uuid' },
|
||||
@@ -43,11 +32,10 @@ class Identity extends Base {
|
||||
modelClass: SamlAuthProvider,
|
||||
join: {
|
||||
from: 'saml_auth_providers.id',
|
||||
to: 'identities.provider_id'
|
||||
to: 'identities.provider_id',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
export default Identity;
|
@@ -1,12 +1,6 @@
|
||||
import Base from './base';
|
||||
|
||||
class Permission extends Base {
|
||||
id: string;
|
||||
roleId: string;
|
||||
action: string;
|
||||
subject: string;
|
||||
conditions: string[];
|
||||
|
||||
static tableName = 'permissions';
|
||||
|
||||
static jsonSchema = {
|
58
packages/backend/src/models/query-builder.js
Normal file
58
packages/backend/src/models/query-builder.js
Normal file
@@ -0,0 +1,58 @@
|
||||
import { Model } from 'objection';
|
||||
|
||||
const DELETED_COLUMN_NAME = 'deleted_at';
|
||||
|
||||
const supportsSoftDeletion = (modelClass) => {
|
||||
return modelClass.jsonSchema.properties.deletedAt;
|
||||
};
|
||||
|
||||
const buildQueryBuidlerForClass = () => {
|
||||
return (modelClass) => {
|
||||
const qb = Model.QueryBuilder.forClass.call(
|
||||
ExtendedQueryBuilder,
|
||||
modelClass
|
||||
);
|
||||
qb.onBuild((builder) => {
|
||||
if (
|
||||
!builder.context().withSoftDeleted &&
|
||||
supportsSoftDeletion(qb.modelClass())
|
||||
) {
|
||||
builder.whereNull(
|
||||
`${qb.modelClass().tableName}.${DELETED_COLUMN_NAME}`
|
||||
);
|
||||
}
|
||||
});
|
||||
return qb;
|
||||
};
|
||||
};
|
||||
|
||||
class ExtendedQueryBuilder extends Model.QueryBuilder {
|
||||
static forClass = buildQueryBuidlerForClass();
|
||||
|
||||
delete() {
|
||||
if (supportsSoftDeletion(this.modelClass())) {
|
||||
return this.patch({
|
||||
[DELETED_COLUMN_NAME]: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
|
||||
return super.delete();
|
||||
}
|
||||
|
||||
hardDelete() {
|
||||
return super.delete();
|
||||
}
|
||||
|
||||
withSoftDeleted() {
|
||||
this.context().withSoftDeleted = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
restore() {
|
||||
return this.patch({
|
||||
[DELETED_COLUMN_NAME]: null,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default ExtendedQueryBuilder;
|
@@ -1,71 +0,0 @@
|
||||
import {
|
||||
Model,
|
||||
Page,
|
||||
ModelClass,
|
||||
PartialModelObject,
|
||||
ForClassMethod,
|
||||
AnyQueryBuilder,
|
||||
} from 'objection';
|
||||
|
||||
const DELETED_COLUMN_NAME = 'deleted_at';
|
||||
|
||||
const supportsSoftDeletion = (modelClass: ModelClass<any>) => {
|
||||
return modelClass.jsonSchema.properties.deletedAt;
|
||||
}
|
||||
|
||||
const buildQueryBuidlerForClass = (): ForClassMethod => {
|
||||
return (modelClass) => {
|
||||
const qb: AnyQueryBuilder = Model.QueryBuilder.forClass.call(
|
||||
ExtendedQueryBuilder,
|
||||
modelClass
|
||||
);
|
||||
qb.onBuild((builder) => {
|
||||
if (!builder.context().withSoftDeleted && supportsSoftDeletion(qb.modelClass())) {
|
||||
builder.whereNull(
|
||||
`${qb.modelClass().tableName}.${DELETED_COLUMN_NAME}`
|
||||
);
|
||||
}
|
||||
});
|
||||
return qb;
|
||||
};
|
||||
};
|
||||
|
||||
class ExtendedQueryBuilder<M extends Model, R = M[]> extends Model.QueryBuilder<
|
||||
M,
|
||||
R
|
||||
> {
|
||||
ArrayQueryBuilderType!: ExtendedQueryBuilder<M, M[]>;
|
||||
SingleQueryBuilderType!: ExtendedQueryBuilder<M, M>;
|
||||
MaybeSingleQueryBuilderType!: ExtendedQueryBuilder<M, M | undefined>;
|
||||
NumberQueryBuilderType!: ExtendedQueryBuilder<M, number>;
|
||||
PageQueryBuilderType!: ExtendedQueryBuilder<M, Page<M>>;
|
||||
|
||||
static forClass: ForClassMethod = buildQueryBuidlerForClass();
|
||||
|
||||
delete() {
|
||||
if (supportsSoftDeletion(this.modelClass())) {
|
||||
return this.patch({
|
||||
[DELETED_COLUMN_NAME]: new Date().toISOString(),
|
||||
} as unknown as PartialModelObject<M>);
|
||||
}
|
||||
|
||||
return super.delete();
|
||||
}
|
||||
|
||||
hardDelete() {
|
||||
return super.delete();
|
||||
}
|
||||
|
||||
withSoftDeleted() {
|
||||
this.context().withSoftDeleted = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
restore() {
|
||||
return this.patch({
|
||||
[DELETED_COLUMN_NAME]: null,
|
||||
} as unknown as PartialModelObject<M>);
|
||||
}
|
||||
}
|
||||
|
||||
export default ExtendedQueryBuilder;
|
@@ -3,13 +3,6 @@ import Permission from './permission';
|
||||
import User from './user';
|
||||
|
||||
class Role extends Base {
|
||||
id!: string;
|
||||
name!: string;
|
||||
key: string;
|
||||
description: string;
|
||||
users?: User[];
|
||||
permissions?: Permission[];
|
||||
|
||||
static tableName = 'roles';
|
||||
|
||||
static jsonSchema = {
|
@@ -1,25 +1,10 @@
|
||||
import { URL } from 'node:url';
|
||||
import type { SamlConfig } from '@node-saml/passport-saml';
|
||||
import appConfig from '../config/app';
|
||||
import Base from './base';
|
||||
import Identity from './identity.ee';
|
||||
import SamlAuthProvidersRoleMapping from './saml-auth-providers-role-mapping.ee';
|
||||
|
||||
class SamlAuthProvider extends Base {
|
||||
id!: string;
|
||||
name: string;
|
||||
certificate: string;
|
||||
signatureAlgorithm: SamlConfig['signatureAlgorithm'];
|
||||
issuer: string;
|
||||
entryPoint: string;
|
||||
firstnameAttributeName: string;
|
||||
surnameAttributeName: string;
|
||||
emailAttributeName: string;
|
||||
roleAttributeName: string;
|
||||
defaultRoleId: string;
|
||||
active: boolean;
|
||||
samlAuthProvidersRoleMappings?: SamlAuthProvidersRoleMapping[];
|
||||
|
||||
static tableName = 'saml_auth_providers';
|
||||
|
||||
static jsonSchema = {
|
||||
@@ -83,7 +68,7 @@ class SamlAuthProvider extends Base {
|
||||
return new URL(`/login/saml/${this.issuer}`, appConfig.baseUrl).toString();
|
||||
}
|
||||
|
||||
get config(): SamlConfig {
|
||||
get config() {
|
||||
const callbackUrl = new URL(
|
||||
`/login/saml/${this.issuer}/callback`,
|
||||
appConfig.baseUrl
|
@@ -2,11 +2,6 @@ import Base from './base';
|
||||
import SamlAuthProvider from './saml-auth-provider.ee';
|
||||
|
||||
class SamlAuthProvidersRoleMapping extends Base {
|
||||
id!: string;
|
||||
samlAuthProviderId: string;
|
||||
roleId: string;
|
||||
remoteRoleName: string;
|
||||
|
||||
static tableName = 'saml_auth_providers_role_mappings';
|
||||
|
||||
static jsonSchema = {
|
@@ -1,7 +1,5 @@
|
||||
import { URL } from 'node:url';
|
||||
import { QueryContext, ModelOptions } from 'objection';
|
||||
import get from 'lodash.get';
|
||||
import type { IJSONObject, IStep } from '@automatisch/types';
|
||||
import Base from './base';
|
||||
import App from './app';
|
||||
import Flow from './flow';
|
||||
@@ -11,20 +9,6 @@ import Telemetry from '../helpers/telemetry';
|
||||
import appConfig from '../config/app';
|
||||
|
||||
class Step extends Base {
|
||||
id!: string;
|
||||
flowId!: string;
|
||||
key?: string;
|
||||
appKey?: string;
|
||||
type!: IStep['type'];
|
||||
connectionId?: string;
|
||||
status: 'incomplete' | 'completed';
|
||||
position!: number;
|
||||
parameters: IJSONObject;
|
||||
connection?: Connection;
|
||||
flow: Flow;
|
||||
executionSteps: ExecutionStep[];
|
||||
webhookPath?: string;
|
||||
|
||||
static tableName = 'steps';
|
||||
|
||||
static jsonSchema = {
|
||||
@@ -100,18 +84,18 @@ class Step extends Base {
|
||||
|
||||
if (!triggerCommand) return null;
|
||||
|
||||
const {
|
||||
useSingletonWebhook,
|
||||
singletonWebhookRefValueParameter,
|
||||
type,
|
||||
} = triggerCommand;
|
||||
const { useSingletonWebhook, singletonWebhookRefValueParameter, type } =
|
||||
triggerCommand;
|
||||
|
||||
const isWebhook = type === 'webhook';
|
||||
|
||||
if (!isWebhook) return null;
|
||||
|
||||
if (singletonWebhookRefValueParameter) {
|
||||
const parameterValue = get(this.parameters, singletonWebhookRefValueParameter);
|
||||
const parameterValue = get(
|
||||
this.parameters,
|
||||
singletonWebhookRefValueParameter
|
||||
);
|
||||
return `/webhooks/connections/${this.connectionId}/${parameterValue}`;
|
||||
}
|
||||
|
||||
@@ -131,21 +115,21 @@ class Step extends Base {
|
||||
return webhookUrl;
|
||||
}
|
||||
|
||||
async $afterInsert(queryContext: QueryContext) {
|
||||
async $afterInsert(queryContext) {
|
||||
await super.$afterInsert(queryContext);
|
||||
Telemetry.stepCreated(this);
|
||||
}
|
||||
|
||||
async $afterUpdate(opt: ModelOptions, queryContext: QueryContext) {
|
||||
async $afterUpdate(opt, queryContext) {
|
||||
await super.$afterUpdate(opt, queryContext);
|
||||
Telemetry.stepUpdated(this);
|
||||
}
|
||||
|
||||
get isTrigger(): boolean {
|
||||
get isTrigger() {
|
||||
return this.type === 'trigger';
|
||||
}
|
||||
|
||||
get isAction(): boolean {
|
||||
get isAction() {
|
||||
return this.type === 'action';
|
||||
}
|
||||
|
@@ -5,20 +5,6 @@ import { DateTime } from 'luxon';
|
||||
import { getPlanById } from '../helpers/billing/plans.ee';
|
||||
|
||||
class Subscription extends Base {
|
||||
id!: string;
|
||||
userId!: string;
|
||||
paddleSubscriptionId!: string;
|
||||
paddlePlanId!: string;
|
||||
updateUrl!: string;
|
||||
cancelUrl!: string;
|
||||
status!: string;
|
||||
nextBillAmount!: string;
|
||||
nextBillDate!: string;
|
||||
lastBillDate?: string;
|
||||
cancellationEffectiveDate?: string;
|
||||
usageData?: UsageData[];
|
||||
currentUsageData?: UsageData;
|
||||
|
||||
static tableName = 'subscriptions';
|
||||
|
||||
static jsonSchema = {
|
||||
@@ -87,7 +73,7 @@ class Subscription extends Base {
|
||||
return (
|
||||
this.status === 'deleted' &&
|
||||
Number(this.cancellationEffectiveDate) >
|
||||
DateTime.now().startOf('day').toMillis()
|
||||
DateTime.now().startOf('day').toMillis()
|
||||
);
|
||||
}
|
||||
|
@@ -4,14 +4,6 @@ import User from './user';
|
||||
import Subscription from './subscription.ee';
|
||||
|
||||
class UsageData extends Base {
|
||||
id!: string;
|
||||
userId!: string;
|
||||
subscriptionId?: string;
|
||||
consumedTaskCount!: number;
|
||||
nextResetAt!: string;
|
||||
subscription?: Subscription;
|
||||
user?: User;
|
||||
|
||||
static tableName = 'usage_data';
|
||||
|
||||
static jsonSchema = {
|
@@ -1,7 +1,6 @@
|
||||
import bcrypt from 'bcrypt';
|
||||
import { DateTime } from 'luxon';
|
||||
import crypto from 'node:crypto';
|
||||
import { ModelOptions, QueryContext } from 'objection';
|
||||
|
||||
import appConfig from '../config/app';
|
||||
import { hasValidLicense } from '../helpers/license.ee';
|
||||
@@ -12,33 +11,12 @@ import Execution from './execution';
|
||||
import Flow from './flow';
|
||||
import Identity from './identity.ee';
|
||||
import Permission from './permission';
|
||||
import ExtendedQueryBuilder from './query-builder';
|
||||
import Role from './role';
|
||||
import Step from './step';
|
||||
import Subscription from './subscription.ee';
|
||||
import UsageData from './usage-data.ee';
|
||||
|
||||
class User extends Base {
|
||||
id!: string;
|
||||
fullName!: string;
|
||||
email!: string;
|
||||
roleId: string;
|
||||
password!: string;
|
||||
resetPasswordToken: string;
|
||||
resetPasswordTokenSentAt: string;
|
||||
trialExpiryDate: string;
|
||||
connections?: Connection[];
|
||||
flows?: Flow[];
|
||||
steps?: Step[];
|
||||
executions?: Execution[];
|
||||
usageData?: UsageData[];
|
||||
currentUsageData?: UsageData;
|
||||
subscriptions?: Subscription[];
|
||||
currentSubscription?: Subscription;
|
||||
role: Role;
|
||||
permissions: Permission[];
|
||||
identities: Identity[];
|
||||
|
||||
static tableName = 'users';
|
||||
|
||||
static jsonSchema = {
|
||||
@@ -116,7 +94,7 @@ class User extends Base {
|
||||
from: 'usage_data.user_id',
|
||||
to: 'users.id',
|
||||
},
|
||||
filter(builder: ExtendedQueryBuilder<UsageData>) {
|
||||
filter(builder) {
|
||||
builder.orderBy('created_at', 'desc').limit(1).first();
|
||||
},
|
||||
},
|
||||
@@ -135,7 +113,7 @@ class User extends Base {
|
||||
from: 'subscriptions.user_id',
|
||||
to: 'users.id',
|
||||
},
|
||||
filter(builder: ExtendedQueryBuilder<Subscription>) {
|
||||
filter(builder) {
|
||||
builder.orderBy('created_at', 'desc').limit(1).first();
|
||||
},
|
||||
},
|
||||
@@ -165,7 +143,7 @@ class User extends Base {
|
||||
},
|
||||
});
|
||||
|
||||
login(password: string) {
|
||||
login(password) {
|
||||
return bcrypt.compare(password, this.password);
|
||||
}
|
||||
|
||||
@@ -176,7 +154,7 @@ class User extends Base {
|
||||
await this.$query().patch({ resetPasswordToken, resetPasswordTokenSentAt });
|
||||
}
|
||||
|
||||
async resetPassword(password: string) {
|
||||
async resetPassword(password) {
|
||||
return await this.$query().patch({
|
||||
resetPasswordToken: null,
|
||||
resetPasswordTokenSentAt: null,
|
||||
@@ -235,9 +213,7 @@ class User extends Base {
|
||||
return false;
|
||||
}
|
||||
|
||||
const expiryDate = DateTime.fromJSDate(
|
||||
this.trialExpiryDate as unknown as Date
|
||||
);
|
||||
const expiryDate = DateTime.fromJSDate(this.trialExpiryDate);
|
||||
const now = DateTime.now();
|
||||
|
||||
return now < expiryDate;
|
||||
@@ -261,7 +237,7 @@ class User extends Base {
|
||||
return currentUsageData.consumedTaskCount < plan.quota;
|
||||
}
|
||||
|
||||
async $beforeInsert(queryContext: QueryContext) {
|
||||
async $beforeInsert(queryContext) {
|
||||
await super.$beforeInsert(queryContext);
|
||||
|
||||
this.email = this.email.toLowerCase();
|
||||
@@ -272,7 +248,7 @@ class User extends Base {
|
||||
}
|
||||
}
|
||||
|
||||
async $beforeUpdate(opt: ModelOptions, queryContext: QueryContext) {
|
||||
async $beforeUpdate(opt, queryContext) {
|
||||
await super.$beforeUpdate(opt, queryContext);
|
||||
|
||||
if (this.email) {
|
||||
@@ -282,7 +258,7 @@ class User extends Base {
|
||||
await this.generateHash();
|
||||
}
|
||||
|
||||
async $afterInsert(queryContext: QueryContext) {
|
||||
async $afterInsert(queryContext) {
|
||||
await super.$afterInsert(queryContext);
|
||||
|
||||
if (appConfig.isCloud) {
|
||||
@@ -294,7 +270,7 @@ class User extends Base {
|
||||
}
|
||||
}
|
||||
|
||||
async $afterFind(): Promise<any> {
|
||||
async $afterFind() {
|
||||
if (await hasValidLicense()) return this;
|
||||
|
||||
if (Array.isArray(this.permissions)) {
|
||||
@@ -313,26 +289,26 @@ class User extends Base {
|
||||
return this;
|
||||
}
|
||||
|
||||
get ability(): ReturnType<typeof userAbility> {
|
||||
get ability() {
|
||||
return userAbility(this);
|
||||
}
|
||||
|
||||
can(action: string, subject: string) {
|
||||
can(action, subject) {
|
||||
const can = this.ability.can(action, subject);
|
||||
|
||||
if (!can) throw new Error('Not authorized!');
|
||||
|
||||
const relevantRule = this.ability.relevantRuleFor(action, subject);
|
||||
|
||||
const conditions = (relevantRule?.conditions as string[]) || [];
|
||||
const conditionMap: Record<string, true> = Object.fromEntries(
|
||||
const conditions = relevantRule?.conditions || [];
|
||||
const conditionMap = Object.fromEntries(
|
||||
conditions.map((condition) => [condition, true])
|
||||
);
|
||||
|
||||
return conditionMap;
|
||||
}
|
||||
|
||||
cannot(action: string, subject: string) {
|
||||
cannot(action, subject) {
|
||||
const cannot = this.ability.cannot(action, subject);
|
||||
|
||||
if (cannot) throw new Error('Not authorized!');
|
Reference in New Issue
Block a user